diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..075b254 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.quarto/ diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/_quarto.yml b/_quarto.yml new file mode 100644 index 0000000..dc3a8b9 --- /dev/null +++ b/_quarto.yml @@ -0,0 +1,35 @@ +project: + type: website + +website: + title: "Cary Introduction to Python" + navbar: + background: primary + search: true + left: + - text: "Home" + file: index.qmd + sidebar: + contents: + - notebooks/introduction_to_data_analysis.ipynb + - notebooks/hypothesis_testing.ipynb + - notebooks/python_101.ipynb + - notebooks/python_errors.ipynb + - notebooks/reading_local_files.ipynb + - notebooks/making_dataframes_from_api_requests.ipynb + - notebooks/wide_vs_long.ipynb + - notebooks/cleaning_data.ipynb + - notebooks/handling_data_issues.ipynb + - notebooks/intro_to_plotly_express.ipynb + - notebooks/introducing_matplotlib.ipynb + - notebooks/plotting_with_pandas.ipynb + - notebooks/pandas_plotting_module.ipynb + +format: + html: + theme: cosmo + toc: true + +output-dir: docs +render: + - notebooks/*.ipynb \ No newline at end of file diff --git a/_site/.nojekyll b/_site/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/_site/CONTRIBUTING.html b/_site/CONTRIBUTING.html new file mode 100644 index 0000000..9fa6e2f --- /dev/null +++ b/_site/CONTRIBUTING.html @@ -0,0 +1,646 @@ + + + + + + + + + +Cary Introduction to Python + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ + + + +
+

Contributing to Cary’s Introduction to Python Repository

+

Thank you for taking the time to contribute to this project. We’re excited that you’re taking the time to become involved in this project.

+
+

Types of Contributions We’re Looking For

+
    +
  • Demonstrations of Python packages
  • +
  • Enriching documentation to existing files
  • +
+
+
+

Ground Rules & Expectations

+

Before we get started, here are a few things we expect from you (and that you should expect from others):

+
    +
  • Be kind and thoughtful in your conversations around this project. We all come from different backgrounds and projects, which means we likely have different perspectives on “how open source is done.” Try to listen to others rather than convince them that your way is correct.
  • +
  • When adding content, please consider if it is widely valuable. Please don’t add references or links to things you or your employer have created, as others will do so if they appreciate it.
  • +
+
+
+

How to Contribute

+

If you’d like to contribute, start by searching through the pull requests to see whether someone else has raised a similar idea or question.

+

If you don’t see your idea listed, and you think it fits into the goals of this guide, open a pull request.

+
+
+

Community

+

Discussions about the Open Source Guides take place on this repository’s Issues and Pull Requests sections. Anybody is welcome to join these conversations.

+

Wherever possible, do not take these conversations to private channels, including contacting the maintainers directly. Keeping communication public means everybody can benefit and learn from the conversation.

+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/SECURITY.html b/_site/SECURITY.html new file mode 100644 index 0000000..e02e94e --- /dev/null +++ b/_site/SECURITY.html @@ -0,0 +1,621 @@ + + + + + + + + + +Cary Introduction to Python + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ + + + +
+

Security Policy

+
+

Reporting a Vulnerability

+

File the vulnerability as an issue.

+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/images/learning.gif b/_site/images/learning.gif new file mode 100644 index 0000000..05213a5 Binary files /dev/null and b/_site/images/learning.gif differ diff --git a/_site/images/type-i-and-ii-error-2.png b/_site/images/type-i-and-ii-error-2.png new file mode 100644 index 0000000..cc77f71 Binary files /dev/null and b/_site/images/type-i-and-ii-error-2.png differ diff --git a/_site/index.html b/_site/index.html new file mode 100644 index 0000000..6d207a9 --- /dev/null +++ b/_site/index.html @@ -0,0 +1,950 @@ + + + + + + + + + +Cary Introduction to Python + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ + + + +
+

Cary Introduction to Python & Anaconda

+

Nbviewer Open In Colab

+ +
+

Installing This Repository’s Depencies using conda-lock.yml or environment.yml Files

+

You can install project dependencies either using out-of-the-box conda CLI commands, or installing conda-lock to ensure dependencies are solved no matter the platform you are on.

+
conda install -c conda-forge conda-lock
+conda-lock install --name name-of-your-environment conda-lock.yml
+conda activate name-of-your-environment
+
conda env create -n name-of-your-environment --file environment.yml
+conda activate name-of-your-environment
+
+
+

Missing an Imported Module?

+
    +
  1. Install your module using conda install name-of-module in your terminal or Anaconda Prompt
  2. +
  3. Install your module with Anaconda Navigator +
      +
    1. Open Anaconda Navigator
    2. +
    3. Click Environments tab
    4. +
    5. Select the Environment you want to install a module into
    6. +
  4. +
+

Please don’t use python -m pip install name-of-module when installing packages without activating your conda environment via conda activate name-of-environment first.

+

Anaconda’s Explanation of conda & pip if you want a more in-depth explanation.

+
+
+

Anaconda’s Free [with Account Creation] Courses

+ +
+
+

Anaconda Ecosystem

+ +
+

Anaconda Navigator (GUI)

+ +
+

Trying to Explore Packages Not Installed?

+

If your list of “Not Installed” packages is blank, I recommend manually updating Anaconda Navigator.

+
+
+
+

conda (CLI)

+ +
+

Need to Rollback to a Previous Version of a conda?

+

If you are experimenting with your conda base environment and need to restore a previous version of a conda.

+
    +
  1. You can use the conda list command with the --revisions flag to view your conda revision history.
  2. +
  3. You can use the conda install command with the --revision flag with the number that corresponds to the version you want to rollback to.
  4. +
+

bash or Powershell conda list --revisions conda install --revision N # Replace N with the number that corresponds to the version you want to rollback to.

+
+
+

Trying to make conda Faster?

+ +
+
+
+
+

Python 3

+
+

Official Documentation

+
    +
  1. Python.org’s ‘Whetting Your Appetite’
  2. +
  3. Python.org’s Official Python 3 Tutorial
  4. +
  5. Python.org’s Glossary of Terms
  6. +
  7. Python.org’s Style Guide for Python Code
  8. +
+
+
+
+

Python Modules

+
+

polars

+

For in-memory analysis of tabular data and introduces the DataFrame for larger-than-memory datasets and want an API similar to pandas.

+ +
+
+

pandas

+

For in-memory analysis of tabular data and introduces the DataFrame

+ +
+
+

geopandas

+

If you need your DataFrame to be mapped or do geospatial calculations.

+ +
+
+

folium

+

If you need more interactivity from your generated map.

+ +
+
+

cartopy

+

If you need more control over your generated static map projection.

+ +
+
+

xarray

+

If you need your dataset to have more than two dimensions.

+ +
+
+

matplotlib

+

Highly configurable visualization library that other libraries build off of.

+ +
+
+

seaborn

+

High-level library for generating statistical graphics, especially for long data format.

+ +
+
+

plotly.express

+

Generate interactive graphics, with a focus on exploratory analysis with visuals.

+ +
+
+ +
+

Youtube Resources to Check Out

+
    +
  1. Rob Mulla on YouTube for Data Science with Focus on Python +
      +
    1. Playlist: Medallion Python Data Science Coding Videos
    2. +
    3. Playlist: Working with Data in Python
    4. +
  2. +
  3. Conference Talk: So you want to be a Python expert?
  4. +
  5. Conference Talk: 1000x faster data manipulation: vectorizing with pandas and numpy
  6. +
  7. Conference Talk: No More Sad Pandas: Optimizing Pandas Code for Sped and Efficiency
  8. +
  9. Conference Talk: Effective Pandas
  10. +
  11. Conference Tutorial: So You Wanna Be a Pandas Expert?
  12. +
+
+
+

Topics on the Table

+
    +
  • Installing Anaconda’s Package & Environment Manager conda (Command Line Interface Tool) and Anaconda-Navigator (Graphical User Interface Tool) (Best practice when it comes to dependency management for Python and R)
  • +
  • Picking an editor of your choice that supports JupyterNotebooks (Visual Studio Code or JupyterLab)
  • +
  • How do I get data into Python and get descriptive statistics? (reading files with pandas)
  • +
  • Now paint me a picture with the data. (introduction to Plotly & Holoviz Ecosystem)
  • +
  • How do I share this? +
      +
    • Binder if you want interactivity (a little more setup)
    • +
    • nbviewer if you value sharing your rendered files (less setup but not as pretty)
    • +
  • +
+
+
+

Sidequests

+ +
+
+

Contributing

+

If you have anything you want to cover, I’m open to suggestions. Feel free to checkout the contributing guidelines for ways to offer feedback and contribute. My previous experience with python covers web scraping, cleaning data, statistical analysis, and moving data into and out of databases.

+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/cleaning_data.html b/_site/notebooks/cleaning_data.html new file mode 100644 index 0000000..3a0258a --- /dev/null +++ b/_site/notebooks/cleaning_data.html @@ -0,0 +1,3279 @@ + + + + + + + + + +Cary Introduction to Python - Cleaning Data + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ +
+
+

Cleaning Data

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Cleaning Data

+
+

About the data

+

In this notebook, we will using daily temperature data from the National Centers for Environmental Information (NCEI) API. We will use the Global Historical Climatology Network - Daily (GHCND) dataset; see the documentation here.

+

This data was collected from the LaGuardia Airport station in New York City for October 2018. It contains: - the daily minimum temperature (TMIN) - the daily maximum temperature (TMAX) - the daily average temperature (TAVG)

+

Note: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.

+

In addition, we will be using S&P 500 stock market data (obtained using the stock_analysis package and data for bitcoin for 2017 through 2018. For the first edition, the bitcoin data was collected from CoinMarketCap using the stock_analysis package; however, changes in the website led to the necessity of changing the data source to Yahoo! Finance. The bitcoin data that was collected before the CoinMarketCap website change should be equivalent to the historical data that can be viewed on this page.

+
+
+

Setup

+

We need to import pandas and read in the temperature data to get started:

+
+
import pandas as pd
+
+df = pd.read_csv('../data/nyc_temperatures.csv')
+df.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationattributesvalue
02018-10-01T00:00:00TAVGGHCND:USW00014732H,,S,21.2
12018-10-01T00:00:00TMAXGHCND:USW00014732,,W,240025.6
22018-10-01T00:00:00TMINGHCND:USW00014732,,W,240018.3
32018-10-02T00:00:00TAVGGHCND:USW00014732H,,S,22.7
42018-10-02T00:00:00TMAXGHCND:USW00014732,,W,240026.1
+ +
+
+
+
+
+

Renaming Columns

+

We start out with the following columns:

+
+
df.columns
+
+
Index(['date', 'datatype', 'station', 'attributes', 'value'], dtype='object')
+
+
+

We want to rename the value column to indicate it contains the temperature in Celsius and the attributes column to say flags since each value in the comma-delimited string is a different flag about the data collection. For this task, we use the rename() method and pass in a dictionary mapping the column names to their new names. We pass inplace=True to change our original dataframe instead of getting a new one back:

+
+
df.rename(
+    columns={
+        'value': 'temp_C',
+        'attributes': 'flags'
+    }, inplace=True
+)
+
+

Those columns have been successfully renamed:

+
+
df.columns
+
+
Index(['date', 'datatype', 'station', 'flags', 'temp_C'], dtype='object')
+
+
+

We can also perform string operations on the column names with rename():

+
+
df.rename(str.upper, axis='columns').columns
+
+
Index(['DATE', 'DATATYPE', 'STATION', 'FLAGS', 'TEMP_C'], dtype='object')
+
+
+
+
+

Type Conversion

+

The date column is not currently being stored as a datetime:

+
+
df.dtypes
+
+
date         object
+datatype     object
+station      object
+flags        object
+temp_C      float64
+dtype: object
+
+
+

Let’s perform the conversion with pd.to_datetime():

+
+
df.loc[:,'date'] = pd.to_datetime(df.date)
+df.dtypes
+
+
date         object
+datatype     object
+station      object
+flags        object
+temp_C      float64
+dtype: object
+
+
+

Now we get useful information when we use describe() on this column:

+
+
df.date.describe()
+
+
count                      93
+unique                     31
+top       2018-10-01 00:00:00
+freq                        3
+Name: date, dtype: object
+
+
+

We can use tz_localize() on a DatetimeIndex object to convert to a desired timezone:

+
+
pd.date_range(start='2018-10-25', periods=2, freq='D').tz_localize('EST')
+
+
DatetimeIndex(['2018-10-25 00:00:00-05:00', '2018-10-26 00:00:00-05:00'], dtype='datetime64[ns, EST]', freq=None)
+
+
+

This also works with Series/DataFrame objects that have an index of type DatetimeIndex. Let’s read in the CSV again for this example and set the date column to be the index and stored as a datetime:

+
+
eastern = pd.read_csv(
+    '../data/nyc_temperatures.csv', index_col='date', parse_dates=True
+).tz_localize('EST')
+eastern.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datatypestationattributesvalue
date
2018-10-01 00:00:00-05:00TAVGGHCND:USW00014732H,,S,21.2
2018-10-01 00:00:00-05:00TMAXGHCND:USW00014732,,W,240025.6
2018-10-01 00:00:00-05:00TMINGHCND:USW00014732,,W,240018.3
2018-10-02 00:00:00-05:00TAVGGHCND:USW00014732H,,S,22.7
2018-10-02 00:00:00-05:00TMAXGHCND:USW00014732,,W,240026.1
+ +
+
+
+

We can use tz_convert() to convert to another timezone from there. If we convert the Eastern datetimes to UTC, they will now be at 5 AM, since pandas will use the offsets to convert:

+
+
eastern.tz_convert('UTC').head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datatypestationattributesvalue
date
2018-10-01 05:00:00+00:00TAVGGHCND:USW00014732H,,S,21.2
2018-10-01 05:00:00+00:00TMAXGHCND:USW00014732,,W,240025.6
2018-10-01 05:00:00+00:00TMINGHCND:USW00014732,,W,240018.3
2018-10-02 05:00:00+00:00TAVGGHCND:USW00014732H,,S,22.7
2018-10-02 05:00:00+00:00TMAXGHCND:USW00014732,,W,240026.1
+ +
+
+
+

We can change the period of the index as well. We could change the period to be monthly to make it easier to aggregate later.

+

The reason we have to add the parameter within tz_localize() to None for this, is because we’ll get a warning from pandas that our output class PeriodArray doesn’t have time zone information and we’ll lose it.

+
+
eastern.tz_localize(None).to_period('M').index
+
+
PeriodIndex(['2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10'],
+            dtype='period[M]', name='date')
+
+
+

We now get a PeriodIndex object, which we can change back into a DatetimeIndex object with to_timestamp():

+
+
eastern.tz_localize(None).to_period('M').to_timestamp().index
+
+
DatetimeIndex(['2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01'],
+              dtype='datetime64[ns]', name='date', freq=None)
+
+
+

We can use the assign() method for working with multiple columns at once (or creating new ones). Since our date column has already been converted, we need to read in the data again:

+
+
df = pd.read_csv('../data/nyc_temperatures.csv').rename(
+    columns={
+        'value': 'temp_C',
+        'attributes': 'flags'
+    }
+)
+
+new_df = df.assign(
+    date=pd.to_datetime(df.date),
+    temp_F=(df.temp_C * 9/5) + 32
+)
+new_df.dtypes
+
+
date        datetime64[ns]
+datatype            object
+station             object
+flags               object
+temp_C             float64
+temp_F             float64
+dtype: object
+
+
+

The date column now has datetimes and the temp_F column was added:

+
+
new_df.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_F
02018-10-01TAVGGHCND:USW00014732H,,S,21.270.16
12018-10-01TMAXGHCND:USW00014732,,W,240025.678.08
22018-10-01TMINGHCND:USW00014732,,W,240018.364.94
32018-10-02TAVGGHCND:USW00014732H,,S,22.772.86
42018-10-02TMAXGHCND:USW00014732,,W,240026.178.98
+ +
+
+
+

We can also use astype() to perform conversions. Let’s create columns of the integer portion of the temperatures in Celsius and Fahrenheit. We will use lambda functions (first introduced in Chapter 2, Working with Pandas DataFrames), so that we can use the values being created in the temp_F column to calculate the temp_F_whole column. It is very common (and useful) to use lambda functions with assign():

+
+
df = df.assign(
+    date=lambda x: pd.to_datetime(x.date),
+    temp_C_whole=lambda x: x.temp_C.astype('int'),
+    temp_F=lambda x: (x.temp_C * 9/5) + 32,
+    temp_F_whole=lambda x: x.temp_F.astype('int')
+)
+
+df.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
02018-10-01TAVGGHCND:USW00014732H,,S,21.22170.1670
12018-10-01TMAXGHCND:USW00014732,,W,240025.62578.0878
22018-10-01TMINGHCND:USW00014732,,W,240018.31864.9464
32018-10-02TAVGGHCND:USW00014732H,,S,22.72272.8672
42018-10-02TMAXGHCND:USW00014732,,W,240026.12678.9878
+ +
+
+
+

Creating categories:

+
+
df_with_categories = df.assign(
+    station=df.station.astype('category'),
+    datatype=df.datatype.astype('category')
+)
+df_with_categories.dtypes
+
+
date            datetime64[ns]
+datatype              category
+station               category
+flags                   object
+temp_C                 float64
+temp_C_whole             int32
+temp_F                 float64
+temp_F_whole             int32
+dtype: object
+
+
+
+
df_with_categories.describe(include='category')
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datatypestation
count9393
unique31
topTAVGGHCND:USW00014732
freq3193
+ +
+
+
+

Our categories have no order, but this is something that pandas supports:

+
+
pd.Categorical(
+    ['med', 'med', 'low', 'high'], 
+    categories=['low', 'med', 'high'],
+    ordered=True
+)
+
+
['med', 'med', 'low', 'high']
+Categories (3, object): ['low' < 'med' < 'high']
+
+
+
+
+

Reordering, reindexing, and sorting

+

Say we want to find the days that reached the hottest temperatures in the weather data; we can sort our values by the temp_C column with the largest on top to find this:

+
+
df[df.datatype == 'TMAX'].sort_values(by='temp_C', ascending=False).head(10)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
192018-10-07TMAXGHCND:USW00014732,,W,240027.82782.0482
282018-10-10TMAXGHCND:USW00014732,,W,240027.82782.0482
312018-10-11TMAXGHCND:USW00014732,,W,240026.72680.0680
102018-10-04TMAXGHCND:USW00014732,,W,240026.12678.9878
42018-10-02TMAXGHCND:USW00014732,,W,240026.12678.9878
12018-10-01TMAXGHCND:USW00014732,,W,240025.62578.0878
252018-10-09TMAXGHCND:USW00014732,,W,240025.62578.0878
72018-10-03TMAXGHCND:USW00014732,,W,240025.02577.0077
132018-10-05TMAXGHCND:USW00014732,,W,240022.82273.0473
222018-10-08TMAXGHCND:USW00014732,,W,240022.82273.0473
+ +
+
+
+

However, this isn’t perfect because we have some ties, and they aren’t sorted consistently. In the first tie between the 7th and the 10th, the earlier date comes first, but the opposite is true with the tie between the 4th and the 2nd. We can use other columns to break ties and specify how to sort each with ascending. Let’s break ties with the date column and show earlier dates before later ones:

+
+
df[df.datatype == 'TMAX'].sort_values(by=['temp_C', 'date'], ascending=[False, True]).head(10)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
192018-10-07TMAXGHCND:USW00014732,,W,240027.82782.0482
282018-10-10TMAXGHCND:USW00014732,,W,240027.82782.0482
312018-10-11TMAXGHCND:USW00014732,,W,240026.72680.0680
42018-10-02TMAXGHCND:USW00014732,,W,240026.12678.9878
102018-10-04TMAXGHCND:USW00014732,,W,240026.12678.9878
12018-10-01TMAXGHCND:USW00014732,,W,240025.62578.0878
252018-10-09TMAXGHCND:USW00014732,,W,240025.62578.0878
72018-10-03TMAXGHCND:USW00014732,,W,240025.02577.0077
132018-10-05TMAXGHCND:USW00014732,,W,240022.82273.0473
222018-10-08TMAXGHCND:USW00014732,,W,240022.82273.0473
+ +
+
+
+

Notice that the index was jumbled in the past 2 results. Here, our index only stores the row number in the original data, but we may not need to keep track of that information. In this case, we can pass in ignore_index=True to get a new index after sorting:

+
+
df[df.datatype == 'TMAX'].sort_values(by=['temp_C', 'date'], ascending=[False, True], ignore_index=True).head(10)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
02018-10-07TMAXGHCND:USW00014732,,W,240027.82782.0482
12018-10-10TMAXGHCND:USW00014732,,W,240027.82782.0482
22018-10-11TMAXGHCND:USW00014732,,W,240026.72680.0680
32018-10-02TMAXGHCND:USW00014732,,W,240026.12678.9878
42018-10-04TMAXGHCND:USW00014732,,W,240026.12678.9878
52018-10-01TMAXGHCND:USW00014732,,W,240025.62578.0878
62018-10-09TMAXGHCND:USW00014732,,W,240025.62578.0878
72018-10-03TMAXGHCND:USW00014732,,W,240025.02577.0077
82018-10-05TMAXGHCND:USW00014732,,W,240022.82273.0473
92018-10-08TMAXGHCND:USW00014732,,W,240022.82273.0473
+ +
+
+
+

When just looking for the n-largest values, rather than wanting to sort all the data, we can use nlargest():

+
+
df[df.datatype == 'TAVG'].nlargest(n=10, columns='temp_C')
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
272018-10-10TAVGGHCND:USW00014732H,,S,23.82374.8474
302018-10-11TAVGGHCND:USW00014732H,,S,23.42374.1274
182018-10-07TAVGGHCND:USW00014732H,,S,22.82273.0473
32018-10-02TAVGGHCND:USW00014732H,,S,22.72272.8672
62018-10-03TAVGGHCND:USW00014732H,,S,21.82171.2471
242018-10-09TAVGGHCND:USW00014732H,,S,21.82171.2471
92018-10-04TAVGGHCND:USW00014732H,,S,21.32170.3470
02018-10-01TAVGGHCND:USW00014732H,,S,21.22170.1670
212018-10-08TAVGGHCND:USW00014732H,,S,20.92069.6269
122018-10-05TAVGGHCND:USW00014732H,,S,20.32068.5468
+ +
+
+
+

We use nsmallest() for the n-smallest values.

+
+
df.nsmallest(n=5, columns=['temp_C', 'date'])
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
652018-10-22TMINGHCND:USW00014732,,W,24005.6542.0842
772018-10-26TMINGHCND:USW00014732,,W,24005.6542.0842
622018-10-21TMINGHCND:USW00014732,,W,24006.1642.9842
742018-10-25TMINGHCND:USW00014732,,W,24006.1642.9842
532018-10-18TMINGHCND:USW00014732,,W,24006.7644.0644
+ +
+
+
+

The sample() method will give us rows (or columns with axis=1) at random. We can provide a seed (random_state) to make this reproducible. The index after we do this is jumbled:

+
+
df.sample(5, random_state=0).index
+
+
Index([2, 30, 55, 16, 13], dtype='int64')
+
+
+

We can use sort_index() to order it again:

+
+
df.sample(5, random_state=0).sort_index().index
+
+
Index([2, 13, 16, 30, 55], dtype='int64')
+
+
+

The sort_index() method can also sort columns alphabetically:

+
+
df.sort_index(axis=1).head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datatypedateflagsstationtemp_Ctemp_C_wholetemp_Ftemp_F_whole
0TAVG2018-10-01H,,S,GHCND:USW0001473221.22170.1670
1TMAX2018-10-01,,W,2400GHCND:USW0001473225.62578.0878
2TMIN2018-10-01,,W,2400GHCND:USW0001473218.31864.9464
3TAVG2018-10-02H,,S,GHCND:USW0001473222.72272.8672
4TMAX2018-10-02,,W,2400GHCND:USW0001473226.12678.9878
+ +
+
+
+

This can make selection with loc easier for many columns:

+
+
df.sort_index(axis=1).head().loc[:,'temp_C':'temp_F_whole']
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
temp_Ctemp_C_wholetemp_Ftemp_F_whole
021.22170.1670
125.62578.0878
218.31864.9464
322.72272.8672
426.12678.9878
+ +
+
+
+

We must sort the index to compare two dataframes. If the index is different, but the data is the same, they will be marked not-equal:

+
+
df.equals(df.sort_values(by='temp_C'))
+
+
False
+
+
+

Sorting the index solves this issue:

+
+
df.equals(df.sort_values(by='temp_C').sort_index())
+
+
True
+
+
+

Let’s set the date column as our index:

+
+
df.set_index('date', inplace=True)
+df.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
date
2018-10-01TAVGGHCND:USW00014732H,,S,21.22170.1670
2018-10-01TMAXGHCND:USW00014732,,W,240025.62578.0878
2018-10-01TMINGHCND:USW00014732,,W,240018.31864.9464
2018-10-02TAVGGHCND:USW00014732H,,S,22.72272.8672
2018-10-02TMAXGHCND:USW00014732,,W,240026.12678.9878
+ +
+
+
+

Now that we have an index of type DatetimeIndex, we can do datetime slicing and indexing. As long as we provide a date format that pandas understands, we can grab the data. To select all of 2018, we simply use df.loc['2018'], for the fourth quarter of 2018 we can use df.loc['2018-Q4'], grabbing October is as simple as using df.loc['2018-10']; these can also be combined to build ranges. Let’s grab October 11, 2018 through October 12, 2018 (inclusive of both endpoints)—note that using loc[] is optional for ranges:

+
+
df['2018-10-11':'2018-10-12']
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
date
2018-10-11TAVGGHCND:USW00014732H,,S,23.42374.1274
2018-10-11TMAXGHCND:USW00014732,,W,240026.72680.0680
2018-10-11TMINGHCND:USW00014732,,W,240021.72171.0671
2018-10-12TAVGGHCND:USW00014732H,,S,18.31864.9464
2018-10-12TMAXGHCND:USW00014732,,W,240022.22271.9671
2018-10-12TMINGHCND:USW00014732,,W,240012.21253.9653
+ +
+
+
+

We can also use reset_index() to get a fresh index and move our current index into a column for safe keeping. This is especially useful if we had data, such as the date, in the index that we don’t want to lose:

+
+
df['2018-10-11':'2018-10-12'].reset_index()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
02018-10-11TAVGGHCND:USW00014732H,,S,23.42374.1274
12018-10-11TMAXGHCND:USW00014732,,W,240026.72680.0680
22018-10-11TMINGHCND:USW00014732,,W,240021.72171.0671
32018-10-12TAVGGHCND:USW00014732H,,S,18.31864.9464
42018-10-12TMAXGHCND:USW00014732,,W,240022.22271.9671
52018-10-12TMINGHCND:USW00014732,,W,240012.21253.9653
+ +
+
+
+

Reindexing allows us to conform our axis to contain a given set of labels. Let’s turn to the S&P 500 stock data in the sp500.csv file to see an example of this. Notice we only have data for trading days (weekdays, excluding holidays):

+
+
sp = pd.read_csv(
+    '../data/sp500.csv', index_col='date', parse_dates=True
+).drop(columns=['adj_close'])
+
+sp.head(10).assign(
+    day_of_week=lambda x: x.index.day_name()
+)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
highlowopenclosevolumeday_of_week
date
2017-01-032263.8798832245.1298832251.5700682257.8300783770530000Tuesday
2017-01-042272.8200682261.6000982261.6000982270.7500003764890000Wednesday
2017-01-052271.5000002260.4499512268.1799322269.0000003761820000Thursday
2017-01-062282.1000982264.0600592271.1398932276.9799803339890000Friday
2017-01-092275.4899902268.8999022273.5900882268.8999023217610000Monday
2017-01-102279.2700202265.2700202269.7199712268.8999023638790000Tuesday
2017-01-112275.3200682260.8300782268.6000982275.3200683620410000Wednesday
2017-01-122271.7800292254.2500002271.1398932270.4399413462130000Thursday
2017-01-132278.6799322271.5100102272.7399902274.6398933081270000Friday
2017-01-172272.0800782262.8100592269.1398932267.8898933584990000Tuesday
+ +
+
+
+

If we want to look at the value of a portfolio (group of assets) that trade on different days, we need to handle the mismatch in the index. Bitcoin, for example, trades daily. If we sum up all the data we have for each day (aggregations will be covered in chapter 4, so don’t fixate on this part), we get the following:

+
+
bitcoin = pd.read_csv(
+    '../data/bitcoin.csv', index_col='date', parse_dates=True
+).drop(columns=['market_cap'])
+
+# every day's closing price = S&P 500 close + Bitcoin close (same for other metrics)
+portfolio = pd.concat([sp, bitcoin], sort=False).groupby(level='date').sum()
+
+portfolio.head(10).assign(
+    day_of_week=lambda x: x.index.day_name()
+)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
highlowopenclosevolumeday_of_week
date
2017-01-011003.080000958.700000963.660000998.330000147775008Sunday
2017-01-021031.390000996.700000998.6200001021.750000222184992Monday
2017-01-033307.9598833266.7298833273.1700683301.6700783955698000Tuesday
2017-01-043432.2400683306.0000983306.0000983425.4800004109835984Wednesday
2017-01-053462.6000003170.8699513424.9099323282.3800004272019008Thursday
2017-01-063328.9100983148.0000593285.3798933179.1799803691766000Friday
2017-01-07908.590000823.560000903.490000908.590000279550016Saturday
2017-01-08942.720000887.250000908.170000911.200000158715008Sunday
2017-01-093189.1799903148.7099023186.8300883171.7299023359486992Monday
2017-01-103194.1400203166.3300203172.1599713176.5799023754598000Tuesday
+ +
+
+
+

It may not be immediately obvious what is wrong with the previous data, but with a visualization we can easily see the cyclical pattern of drops on the days the stock market is closed. (Don’t worry about the plotting code too much, we will cover it in depth in chapters 5 and 6).

+

We will need to import matplotlib now:

+
+
import matplotlib.pyplot as plt # we use this module for plotting
+from matplotlib.ticker import StrMethodFormatter # for formatting the axis
+
+

Now we can see why we need to reindex:

+
+
# plot the closing price from Q4 2017 through Q2 2018
+ax = portfolio['2017-Q4':'2018-Q2'].plot(
+    y='close', figsize=(15, 5), legend=False,
+    title='Bitcoin + S&P 500 value without accounting for different indices'
+)
+
+# formatting
+ax.set_ylabel('price')
+ax.yaxis.set_major_formatter(StrMethodFormatter('${x:,.0f}'))
+for spine in ['top', 'right']:
+    ax.spines[spine].set_visible(False)
+
+# show the plot
+plt.show()
+
+
+
+

+
+
+
+
+

We need to align the index of the S&P 500 to match bitcoin in order to fix this. We will use the reindex() method, but by default we get NaN for the values that we don’t have data for:

+
+
sp.reindex(bitcoin.index).head(10).assign(
+    day_of_week=lambda x: x.index.day_name()
+)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
highlowopenclosevolumeday_of_week
date
2017-01-01NaNNaNNaNNaNNaNSunday
2017-01-02NaNNaNNaNNaNNaNMonday
2017-01-032263.8798832245.1298832251.5700682257.8300783.770530e+09Tuesday
2017-01-042272.8200682261.6000982261.6000982270.7500003.764890e+09Wednesday
2017-01-052271.5000002260.4499512268.1799322269.0000003.761820e+09Thursday
2017-01-062282.1000982264.0600592271.1398932276.9799803.339890e+09Friday
2017-01-07NaNNaNNaNNaNNaNSaturday
2017-01-08NaNNaNNaNNaNNaNSunday
2017-01-092275.4899902268.8999022273.5900882268.8999023.217610e+09Monday
2017-01-102279.2700202265.2700202269.7199712268.8999023.638790e+09Tuesday
+ +
+
+
+

So now we have rows for every day of the year, but all the weekends and holidays have NaN values. To address this, we can specify how to handle missing values with the method argument. In this case, we want to forward-fill, which will put the weekend and holiday values as the value they had for the Friday (or end of trading week) before:

+
+
sp.reindex(bitcoin.index, method='ffill').head(10)\
+    .assign(day_of_week=lambda x: x.index.day_name())
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
highlowopenclosevolumeday_of_week
date
2017-01-01NaNNaNNaNNaNNaNSunday
2017-01-02NaNNaNNaNNaNNaNMonday
2017-01-032263.8798832245.1298832251.5700682257.8300783.770530e+09Tuesday
2017-01-042272.8200682261.6000982261.6000982270.7500003.764890e+09Wednesday
2017-01-052271.5000002260.4499512268.1799322269.0000003.761820e+09Thursday
2017-01-062282.1000982264.0600592271.1398932276.9799803.339890e+09Friday
2017-01-072282.1000982264.0600592271.1398932276.9799803.339890e+09Saturday
2017-01-082282.1000982264.0600592271.1398932276.9799803.339890e+09Sunday
2017-01-092275.4899902268.8999022273.5900882268.8999023.217610e+09Monday
2017-01-102279.2700202265.2700202269.7199712268.8999023.638790e+09Tuesday
+ +
+
+
+

To isolate the changes happening with the forward-filling, we can use the compare() method. It shows us the values that differ across identically-labeled dataframes (same names and same columns). Here, we can see that only weekends and holidays (Monday, January 16, 2017 was MLK day) have values forward-filled. Notice that consecutive days have the same values.

+
+
sp.reindex(bitcoin.index)\
+    .compare(sp.reindex(bitcoin.index, method='ffill'))\
+    .head(10).assign(day_of_week=lambda x: x.index.day_name())
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
highlowopenclosevolumeday_of_week
selfotherselfotherselfotherselfotherselfother
date
2017-01-07NaN2282.100098NaN2264.060059NaN2271.139893NaN2276.979980NaN3.339890e+09Saturday
2017-01-08NaN2282.100098NaN2264.060059NaN2271.139893NaN2276.979980NaN3.339890e+09Sunday
2017-01-14NaN2278.679932NaN2271.510010NaN2272.739990NaN2274.639893NaN3.081270e+09Saturday
2017-01-15NaN2278.679932NaN2271.510010NaN2272.739990NaN2274.639893NaN3.081270e+09Sunday
2017-01-16NaN2278.679932NaN2271.510010NaN2272.739990NaN2274.639893NaN3.081270e+09Monday
2017-01-21NaN2276.959961NaN2265.010010NaN2269.959961NaN2271.310059NaN3.524970e+09Saturday
2017-01-22NaN2276.959961NaN2265.010010NaN2269.959961NaN2271.310059NaN3.524970e+09Sunday
2017-01-28NaN2299.020020NaN2291.620117NaN2299.020020NaN2294.689941NaN3.135890e+09Saturday
2017-01-29NaN2299.020020NaN2291.620117NaN2299.020020NaN2294.689941NaN3.135890e+09Sunday
2017-02-04NaN2298.310059NaN2287.879883NaN2288.540039NaN2297.419922NaN3.597970e+09Saturday
+ +
+
+
+

This isn’t perfect though. We probably want 0 for the volume traded and to put the closing price for the open, high, low, and close on the days the market is closed:

+

The reason why we’re using np.where(boolean condition, value if True, value if False) within lambda functions in the example below, is that vectorized operations allow us to be faster and more efficient than utilizing for loops to perform calculations on arrays all at once.

+
+
import numpy as np
+
+sp_reindexed = sp.reindex(bitcoin.index).assign(
+    volume=lambda x: x.volume.fillna(0), # put 0 when market is closed
+    close=lambda x: x.close.fillna(method='ffill'), # carry this forward
+    # take the closing price if these aren't available
+    open=lambda x: np.where(x.open.isnull(), x.close, x.open),
+    high=lambda x: np.where(x.high.isnull(), x.close, x.high),
+    low=lambda x: np.where(x.low.isnull(), x.close, x.low)
+)
+sp_reindexed.head(10).assign(
+    day_of_week=lambda x: x.index.day_name()
+)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
highlowopenclosevolumeday_of_week
date
2017-01-01NaNNaNNaNNaN0.000000e+00Sunday
2017-01-02NaNNaNNaNNaN0.000000e+00Monday
2017-01-032263.8798832245.1298832251.5700682257.8300783.770530e+09Tuesday
2017-01-042272.8200682261.6000982261.6000982270.7500003.764890e+09Wednesday
2017-01-052271.5000002260.4499512268.1799322269.0000003.761820e+09Thursday
2017-01-062282.1000982264.0600592271.1398932276.9799803.339890e+09Friday
2017-01-072276.9799802276.9799802276.9799802276.9799800.000000e+00Saturday
2017-01-082276.9799802276.9799802276.9799802276.9799800.000000e+00Sunday
2017-01-092275.4899902268.8999022273.5900882268.8999023.217610e+09Monday
2017-01-102279.2700202265.2700202269.7199712268.8999023.638790e+09Tuesday
+ +
+
+
+

If we create a visualization comparing the reindexed data to the first attempt, we see how reindexing helped maintain the asset value when the market was closed:

+
+
# every day's closing price = S&P 500 close adjusted for market closure + Bitcoin close (same for other metrics)
+fixed_portfolio = sp_reindexed + bitcoin
+
+# plot the reindexed portfolio's closing price from Q4 2017 through Q2 2018
+ax = fixed_portfolio['2017-Q4':'2018-Q2'].plot(
+    y='close', label='reindexed portfolio of S&P 500 + Bitcoin', figsize=(15, 5), linewidth=2, 
+    title='Reindexed portfolio vs. portfolio with mismatched indices'
+)
+
+# add line for original portfolio for comparison
+portfolio['2017-Q4':'2018-Q2'].plot(
+    y='close', ax=ax, linestyle='--', label='portfolio of S&P 500 + Bitcoin w/o reindexing'
+)
+
+# formatting
+ax.set_ylabel('price')
+ax.yaxis.set_major_formatter(StrMethodFormatter('${x:,.0f}'))
+for spine in ['top', 'right']:
+    ax.spines[spine].set_visible(False)
+
+# show the plot
+plt.show()
+
+
+
+

+
+
+
+
+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/cleaning_data_files/figure-html/cell-38-output-1.png b/_site/notebooks/cleaning_data_files/figure-html/cell-38-output-1.png new file mode 100644 index 0000000..a2de033 Binary files /dev/null and b/_site/notebooks/cleaning_data_files/figure-html/cell-38-output-1.png differ diff --git a/_site/notebooks/cleaning_data_files/figure-html/cell-43-output-1.png b/_site/notebooks/cleaning_data_files/figure-html/cell-43-output-1.png new file mode 100644 index 0000000..7176ca7 Binary files /dev/null and b/_site/notebooks/cleaning_data_files/figure-html/cell-43-output-1.png differ diff --git a/_site/notebooks/handling_data_issues.html b/_site/notebooks/handling_data_issues.html new file mode 100644 index 0000000..62ec83f --- /dev/null +++ b/_site/notebooks/handling_data_issues.html @@ -0,0 +1,2354 @@ + + + + + + + + + +Cary Introduction to Python - Handling duplicate, missing, or invalid data + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ +
+
+

Handling duplicate, missing, or invalid data

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Handling duplicate, missing, or invalid data

+
+

About the data

+

In this notebook, we will using daily weather data that was taken from the National Centers for Environmental Information (NCEI) API and altered to introduce many common problems faced when working with data.

+

Note: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.

+
+
+

Background on the data

+

Data meanings: - PRCP: precipitation in millimeters - SNOW: snowfall in millimeters - SNWD: snow depth in millimeters - TMAX: maximum daily temperature in Celsius - TMIN: minimum daily temperature in Celsius - TOBS: temperature at time of observation in Celsius - WESF: water equivalent of snow in millimeters

+

Some important facts to get our bearings: - According to the National Weather Service, the coldest temperature ever recorded in Central Park was -15°F (-26.1°C) on February 9, 1934: source - The temperature of the Sun’s photosphere is approximately 5,505°C: source

+
+
+

Setup

+

We need to import pandas and read in the dirty data to get started:

+
+
import pandas as pd
+
+df = pd.read_csv('../data/dirty_data.csv')
+
+
+
+

Finding problematic data

+

A good first step is to look at some rows:

+
+
df.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datestationPRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
02018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
12018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
22018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
32018-01-02T00:00:00GHCND:USC002809070.00.0-inf-8.3-16.1-12.2NaNFalse
42018-01-03T00:00:00GHCND:USC002809070.00.0-inf-4.4-13.9-13.3NaNFalse
+ +
+
+
+

Looking at summary statistics can reveal strange or missing values:

+
+
df.describe()
+
+
c:\Users\gpower\AppData\Local\mambaforge\envs\cary_dev\Lib\site-packages\numpy\lib\function_base.py:4573: RuntimeWarning: invalid value encountered in subtract
+  diff_b_a = subtract(b, a)
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESF
count765.000000577.000000577.0765.000000765.000000398.00000011.000000
mean5.3603924.202773NaN2649.175294-15.9143798.63216116.290909
std10.00213825.086077NaN2744.15628124.2428499.8150549.489832
min0.0000000.000000-inf-11.700000-40.000000-16.1000001.800000
25%0.0000000.000000NaN13.300000-40.0000000.1500008.600000
50%0.0000000.000000NaN32.800000-11.1000008.30000019.300000
75%5.8000000.000000NaN5505.0000006.70000018.30000024.900000
max61.700000229.000000inf5505.00000023.90000026.10000028.700000
+ +
+
+
+

The info() method can pinpoint missing values and wrong data types:

+
+
df.info()
+
+
<class 'pandas.core.frame.DataFrame'>
+RangeIndex: 765 entries, 0 to 764
+Data columns (total 10 columns):
+ #   Column             Non-Null Count  Dtype  
+---  ------             --------------  -----  
+ 0   date               765 non-null    object 
+ 1   station            765 non-null    object 
+ 2   PRCP               765 non-null    float64
+ 3   SNOW               577 non-null    float64
+ 4   SNWD               577 non-null    float64
+ 5   TMAX               765 non-null    float64
+ 6   TMIN               765 non-null    float64
+ 7   TOBS               398 non-null    float64
+ 8   WESF               11 non-null     float64
+ 9   inclement_weather  408 non-null    object 
+dtypes: float64(7), object(3)
+memory usage: 59.9+ KB
+
+
+

We can use the isna()/isnull() method of the series to find nulls:

+
+
contain_nulls = df[
+    df.SNOW.isna() | df.SNWD.isna() | df.TOBS.isna()
+    | df.WESF.isna() | df.inclement_weather.isna()
+]
+contain_nulls.shape[0]
+
+
765
+
+
+
+
contain_nulls.head(10)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datestationPRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
02018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
12018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
22018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
32018-01-02T00:00:00GHCND:USC002809070.00.0-inf-8.3-16.1-12.2NaNFalse
42018-01-03T00:00:00GHCND:USC002809070.00.0-inf-4.4-13.9-13.3NaNFalse
52018-01-03T00:00:00GHCND:USC002809070.00.0-inf-4.4-13.9-13.3NaNFalse
62018-01-03T00:00:00GHCND:USC002809070.00.0-inf-4.4-13.9-13.3NaNFalse
72018-01-04T00:00:00?20.6229.0inf5505.0-40.0NaN19.3True
82018-01-04T00:00:00?20.6229.0inf5505.0-40.0NaN19.3True
92018-01-05T00:00:00?0.3NaNNaN5505.0-40.0NaNNaNNaN
+ +
+
+
+

Note that we can’t check if we have NaN like this:

+
+
df[df.inclement_weather == 'NaN'].shape[0]
+
+
0
+
+
+

This is because it is actually np.nan. However, notice this also doesn’t work:

+
+
import numpy as np
+df[df.inclement_weather == np.nan].shape[0]
+
+
0
+
+
+

We have to use one of the methods discussed earlier for this to work:

+
+
df[df.inclement_weather.isna()].shape[0]
+
+
357
+
+
+

We can find -inf/inf by comparing to -np.inf/np.inf:

+
+
df[df.SNWD.isin([-np.inf, np.inf])].shape[0]
+
+
577
+
+
+

Rather than do this for each column, we can write a function that will use a dictionary comprehension to check all the columns for us:

+
+
def get_inf_count(df):
+    """Find the number of inf/-inf values per column in the dataframe"""
+    return {
+        col: df[df[col].isin([np.inf, -np.inf])].shape[0] for col in df.columns
+    }
+
+get_inf_count(df)
+
+
{'date': 0,
+ 'station': 0,
+ 'PRCP': 0,
+ 'SNOW': 0,
+ 'SNWD': 577,
+ 'TMAX': 0,
+ 'TMIN': 0,
+ 'TOBS': 0,
+ 'WESF': 0,
+ 'inclement_weather': 0}
+
+
+

Before we can decide how to handle the infinite values of snow depth, we should look at the summary statistics for snowfall, which forms a big part in determining the snow depth:

+
+
pd.DataFrame({
+    'np.inf Snow Depth': df[df.SNWD == np.inf].SNOW.describe(),
+    '-np.inf Snow Depth': df[df.SNWD == -np.inf].SNOW.describe()
+}).T
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
countmeanstdmin25%50%75%max
np.inf Snow Depth24.0101.04166774.49801813.025.0120.5152.0229.0
-np.inf Snow Depth553.00.0000000.0000000.00.00.00.00.0
+ +
+
+
+

Let’s now look into the date and station columns. We saw the ? for station earlier, so we know that was the other unique value. However, we see that some dates are present 8 times in the data and we only have 324 days meaning we are also missing days:

+
+
df.describe(include='object')
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datestationinclement_weather
count765765408
unique32422
top2018-07-05T00:00:00GHCND:USC00280907False
freq8398384
+ +
+
+
+

We can use the duplicated() method to find duplicate rows:

+
+
df[df.duplicated()].shape[0]
+
+
284
+
+
+

The default for keep is 'first' meaning it won’t show the first row that the duplicated data was seen in; we can pass in False to see it though:

+
+
df[df.duplicated(keep=False)].shape[0]
+
+
482
+
+
+

We can also specify the columns to use:

+
+
df[df.duplicated(['date', 'station'])].shape[0]
+
+
284
+
+
+

Let’s look at a few duplicates. Just in the few values we see here, we know that the top 4 are actually in the data 6 times because by default we aren’t seeing their first occurrence:

+
+
df[df.duplicated()].head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datestationPRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
12018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
22018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
52018-01-03T00:00:00GHCND:USC002809070.00.0-inf-4.4-13.9-13.3NaNFalse
62018-01-03T00:00:00GHCND:USC002809070.00.0-inf-4.4-13.9-13.3NaNFalse
82018-01-04T00:00:00?20.6229.0inf5505.0-40.0NaN19.3True
+ +
+
+
+
+
+

Mitigating Issues

+
+

Handling duplicated data

+

Since we know we have NY weather data and noticed we only had two entries for station, we may decide to drop the station column because we are only interested in the weather data. However, when dealing with duplicate data, we need to think of the ramifications of removing it. Notice we only have data for the WESF column when the station is ?:

+
+
df[df.WESF.notna()].station.unique()
+
+
array(['?'], dtype=object)
+
+
+

If we determine it won’t impact our analysis, we can use drop_duplicates() to remove them:

+
+
# 1. make the date a datetime
+df.date = pd.to_datetime(df.date)
+
+# 2. save this information for later
+station_qm_wesf = df[df.station == '?'].drop_duplicates('date').set_index('date').WESF
+
+# 3. sort ? to the bottom
+df.sort_values('station', ascending=False, inplace=True)
+
+# 4. drop duplicates based on the date column keeping the first occurrence 
+# which will be the valid station if it has data
+df_deduped = df.drop_duplicates('date')
+
+# 5. remove the station column because we are done with it
+df_deduped = df_deduped.drop(columns='station').set_index('date').sort_index()
+
+# 6. take valid station's WESF and fall back on station ? if it is null
+df_deduped = df_deduped.assign(
+    WESF=lambda x: x.WESF.combine_first(station_qm_wesf)
+)
+
+df_deduped.shape
+
+
(324, 8)
+
+
+

Here we used the combine_first() method to coalesce the values to the first non-null entry; this means that if we had data from both stations, we would first take the value provided by the named station and if (and only if) that station was null would we take the value from the station named ?. The following table contains some examples of how this would play out:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
station GHCND:USC00280907station ?result of combine_first()
1171
1NaN1
NaN1717
NaNNaNNaN
+

Check out the 4th row—we have WESF in the correct spot thanks to the index:

+
+
df_deduped.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
date
2018-01-010.00.0-inf5505.0-40.0NaNNaNNaN
2018-01-020.00.0-inf-8.3-16.1-12.2NaNFalse
2018-01-030.00.0-inf-4.4-13.9-13.3NaNFalse
2018-01-0420.6229.0inf5505.0-40.0NaN19.3True
2018-01-0514.2127.0inf-4.4-13.9-13.9NaNTrue
+ +
+
+
+
+
+

Dealing with nulls

+

We could drop nulls, replace them with some arbitrary value, or impute them using the surrounding data. Each of these options may have ramifications, so we must choose wisely.

+

We can use dropna() to drop rows where any column has a null value. The default options leave us hardly any data:

+
+
df_deduped.dropna().shape
+
+
(4, 8)
+
+
+

If we pass how='all', we can choose to only drop rows where everything is null, but this removes nothing:

+
+
df_deduped.dropna(how='all').shape
+
+
(324, 8)
+
+
+

We can use just a subset of columns to determine what to drop with the subset argument:

+
+
df_deduped.dropna(
+    how='all', subset=['inclement_weather', 'SNOW', 'SNWD']
+).shape
+
+
(293, 8)
+
+
+

This can also be performed along columns, and we can also require a certain number of null values before we drop the data:

+
+
df_deduped.dropna(axis='columns', thresh=df_deduped.shape[0] * .75).columns
+
+
Index(['PRCP', 'SNOW', 'SNWD', 'TMAX', 'TMIN', 'TOBS', 'inclement_weather'], dtype='object')
+
+
+

We can choose to fill in the null values instead with fillna():

+
+
df_deduped.loc[:,'WESF'].fillna(0, inplace=True)
+df_deduped.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
date
2018-01-010.00.0-inf5505.0-40.0NaN0.0NaN
2018-01-020.00.0-inf-8.3-16.1-12.20.0False
2018-01-030.00.0-inf-4.4-13.9-13.30.0False
2018-01-0420.6229.0inf5505.0-40.0NaN19.3True
2018-01-0514.2127.0inf-4.4-13.9-13.90.0True
+ +
+
+
+

At this point we have done everything we can without distorting the data. We know that we are missing dates, but if we reindex, we don’t know how to fill in the NaN data. With the weather data, we can’t assume because it snowed one day that it will snow the next or that the temperature will be the same. For this reason, note that the next few examples are just for illustrative purposes only—just because we can do something doesn’t mean we should.

+

That being said, let’s try to address some of remaining issues with the temperature data. We know that when TMAX is the temperature of the Sun, it must be because there was no measured value, so let’s replace it with NaN. We will also do so for TMIN which currently uses -40°C for its placeholder when we know that the coldest temperature ever recorded in NYC was -15°F (-26.1°C) on February 9, 1934:

+
+
df_deduped = df_deduped.assign(
+    TMAX=lambda x: x.TMAX.replace(5505, np.nan),
+    TMIN=lambda x: x.TMIN.replace(-40, np.nan),
+)
+
+

We will also make an assumption that the temperature won’t change drastically day-to-day. Note that this is actually a big assumption, but it will allow us to understand how fillna() works when we provide a strategy through the method parameter. The fillna() method gives us 2 options for the method parameter: - 'ffill' to forward-fill - 'bfill' to back-fill

+

Note that 'nearest' is missing because we are not reindexing.

+

Here, we will use 'ffill' to show how this works:

+
+
df_deduped.assign(
+    TMAX=lambda x: x.TMAX.fillna(method='ffill'),
+    TMIN=lambda x: x.TMIN.fillna(method='ffill')
+).head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
date
2018-01-010.00.0-infNaNNaNNaN0.0NaN
2018-01-020.00.0-inf-8.3-16.1-12.20.0False
2018-01-030.00.0-inf-4.4-13.9-13.30.0False
2018-01-0420.6229.0inf-4.4-13.9NaN19.3True
2018-01-0514.2127.0inf-4.4-13.9-13.90.0True
+ +
+
+
+

We can use np.nan_to_num() to turn np.nan into 0 and -np.inf/np.inf into large negative or positive finite numbers:

+
+
df_deduped.assign(
+    SNWD=lambda x: np.nan_to_num(x.SNWD)
+).head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
date
2018-01-010.00.0-1.797693e+308NaNNaNNaN0.0NaN
2018-01-020.00.0-1.797693e+308-8.3-16.1-12.20.0False
2018-01-030.00.0-1.797693e+308-4.4-13.9-13.30.0False
2018-01-0420.6229.01.797693e+308NaNNaNNaN19.3True
2018-01-0514.2127.01.797693e+308-4.4-13.9-13.90.0True
+ +
+
+
+

Depending on the data we are working with, we can use the clip() method as an alternative to np.nan_to_num(). The clip() method makes it possible to cap values at a specific minimum and/or maximum threshold. Since SNWD can’t be negative, let’s use clip() to enforce a lower bound of zero. To show how the upper bound works, let’s use the value of SNOW:

+
+
df_deduped.assign(
+    SNWD=lambda x: x.SNWD.clip(0, x.SNOW)
+).head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
date
2018-01-010.00.00.0NaNNaNNaN0.0NaN
2018-01-020.00.00.0-8.3-16.1-12.20.0False
2018-01-030.00.00.0-4.4-13.9-13.30.0False
2018-01-0420.6229.0229.0NaNNaNNaN19.3True
2018-01-0514.2127.0127.0-4.4-13.9-13.90.0True
+ +
+
+
+

We can couple fillna() with other types of calculations. Here we replace missing values of TMAX with the median of all TMAX values, TMIN with the median of all TMIN values, and TOBS to the average of the TMAX and TMIN values. Since we place TOBS last, we have access to the imputed values for TMIN and TMAX in the calculation:

+
+
df_deduped.assign(
+    TMAX=lambda x: x.TMAX.fillna(x.TMAX.median()),
+    TMIN=lambda x: x.TMIN.fillna(x.TMIN.median()),
+    # average of TMAX and TMIN
+    TOBS=lambda x: x.TOBS.fillna((x.TMAX + x.TMIN) / 2)
+).head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
date
2018-01-010.00.0-inf14.45.610.00.0NaN
2018-01-020.00.0-inf-8.3-16.1-12.20.0False
2018-01-030.00.0-inf-4.4-13.9-13.30.0False
2018-01-0420.6229.0inf14.45.610.019.3True
2018-01-0514.2127.0inf-4.4-13.9-13.90.0True
+ +
+
+
+

We can also use apply() for running the same calculation across columns. For example, let’s fill all missing values with their rolling 7-day median of their values, setting the number of periods required for the calculation to 0 to ensure we don’t introduce more extra NaN values. Rolling calculations will be covered later on, so this is a preview:

+
+
df_deduped.apply(
+    # rolling calculations will be covered later on, this is a rolling 7-day median
+    # we set min_periods (# of periods required for calculation) to 0 so we always get a result 
+    lambda x: x.fillna(x.rolling(7, min_periods=0).median())
+).head(10)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
date
2018-01-010.00.0-infNaNNaNNaN0.0NaN
2018-01-020.00.0-inf-8.30-16.1-12.200.0False
2018-01-030.00.0-inf-4.40-13.9-13.300.0False
2018-01-0420.6229.0inf-6.35-15.0-12.7519.3True
2018-01-0514.2127.0inf-4.40-13.9-13.900.0True
2018-01-060.00.0-inf-10.00-15.6-15.000.0False
2018-01-070.00.0-inf-11.70-17.2-16.100.0False
2018-01-080.00.0-inf-7.80-16.7-8.300.0False
2018-01-100.00.0-inf5.00-7.8-7.800.0False
2018-01-110.00.0-inf4.40-7.81.100.0False
+ +
+
+
+

The last strategy we could try is interpolation with the interpolate() method. We specify the method parameter with the interpolation strategy to use. There are many options, but we will stick with the default of 'linear', which will treat values as evenly spaced and place missing values in the middle of existing ones. We have some missing data, so we will reindex first. Look at January 9th, which we didn’t have before—the values for TMAX, TMIN, and TOBS are the average of values the day prior (January 8th) and the day after (January 10th):

+
+
df_deduped\
+    .reindex(pd.date_range('2018-01-01', '2018-12-31', freq='D'))\
+    .apply(lambda x: x.interpolate())\
+    .head(10)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
2018-01-010.00.0-infNaNNaNNaN0.0NaN
2018-01-020.00.0-inf-8.3-16.10-12.200.0False
2018-01-030.00.0-inf-4.4-13.90-13.300.0False
2018-01-0420.6229.0inf-4.4-13.90-13.6019.3True
2018-01-0514.2127.0inf-4.4-13.90-13.900.0True
2018-01-060.00.0-inf-10.0-15.60-15.000.0False
2018-01-070.00.0-inf-11.7-17.20-16.100.0False
2018-01-080.00.0-inf-7.8-16.70-8.300.0False
2018-01-090.00.0-inf-1.4-12.25-8.050.0NaN
2018-01-100.00.0-inf5.0-7.80-7.800.0False
+ +
+
+
+ + +
+
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/hypothesis_testing.html b/_site/notebooks/hypothesis_testing.html new file mode 100644 index 0000000..4da7943 --- /dev/null +++ b/_site/notebooks/hypothesis_testing.html @@ -0,0 +1,712 @@ + + + + + + + + + +Cary Introduction to Python - Hypothesis Testing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ +
+
+

Hypothesis Testing

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Hypothesis Testing

+

A hypothesis is a claim or statement about a parameter1. There are two types of statistical hypotheses: - Null Hypothesis - Alternative Hypothesis

+

A null hypothesis is a statement that claims that the parameter is equal to some claimed value. - H0 is the symbol used to denote this. It can be pronounced as “H null”, “H zero” or “H nought” - It always contains one of these operators: \(\ge\), \(\le\), =. - This value is the one to always assume is true.

+

An alternative hypothesis is a statement that claims that the parameter is a different value than the null. - HA or H1 is the symbol used to denote this. It’s always called “alternative hypothesis.” - It always contains one of these operators: \(\gt\), \(\lt\), \(\neq\).

+
+

Steps to Solving a Hypothesis Test Problem

+
    +
  1. Write and label everything.
  2. +
  3. Write hypotheses: +
      +
    • H0: (operator with equal sign)
    • +
    • HA: (operator without equal sign)
    • +
  4. +
  5. Draw graph (bell-curved) +
      +
    • The graph will either be right, left or two tailed.
    • +
  6. +
  7. Carry out the necessary calculations to arrive to a solution. +
      +
    • This can involve solving a t-statisic or z-test.
    • +
  8. +
  9. Write a sentence summarizing the findings. +
      +
    • Usually follows this format: “There is/is not sufficient evidence to support/reject the claim that…”
    • +
  10. +
+
+
+

Rejection Explained

+

Every hypothesis test is rejected or failed to reject. This is because we either have enough data to be able to say the hypothesis is correct, or we don’t have enough data to prove otherwise. To determine this, we compare the significance level to the p-value .

+

The significance level is denoted by \(\alpha\) which measures how strong the evidence must be in order to determine the evidence to be statistically significant.

+

P-value is defined by Investopedia as “a statistical measurement used to validate a hypothesis against observed data.” We’re not going to go in-depth here regarding how the p-value is calculated, but just enough to scratch the surface. This value describes the likelihood of the data occurring randomly. P-values range from 0 to 1 and a smaller p-value denotes a smaller probability that the results occurred randomly.

+
    +
  • If p-value \(\leq\) \(\alpha\), then reject H0.
  • +
  • If p-value \(\gt\) \(\alpha\), then fail to reject H0.
  • +
+
+
+

Determining the Tail of the Curve

+

The trick to remembering where the tail of the curve is by looking at the alternative hypothesis. - If the sign in HA is - \(\neq\): two-tailed - \(\lt\): left-taied - \(\gt\): right-tailed

+
+
+

Errors

+

Sometimes, error occurs with hypothesis testing and there are two types of it: - Type I error - This is known as the false-positive. - It occurs when the null hypothesis is rejected, but it is true. - Type II error - This is known as the false-negative. - It occurs when the null hypothesis is not rejected, but it is false.

+

This table below from Scribbr can be used to determine error type, if any.

+

+

1A parameter is a measure done on an entire population of data.

+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/intro_to_plotly_express.html b/_site/notebooks/intro_to_plotly_express.html new file mode 100644 index 0000000..ca1a5b9 --- /dev/null +++ b/_site/notebooks/intro_to_plotly_express.html @@ -0,0 +1,1007 @@ + + + + + + + + + +Cary Introduction to Python + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ + + + +
+
import plotly.express as px
+
+df = px.data.gapminder().query("year==2007")
+
+
+
df.columns
+
+
Index(['country', 'continent', 'year', 'lifeExp', 'pop', 'gdpPercap',
+       'iso_alpha', 'iso_num'],
+      dtype='object')
+
+
+
+
df.describe()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearlifeExppopgdpPercapiso_num
count142.0142.0000001.420000e+02142.000000142.000000
mean2007.067.0074234.402122e+0711680.071820425.880282
std0.012.0730211.476214e+0812859.937337249.111541
min2007.039.6130001.995790e+05277.5518594.000000
25%2007.057.1602504.508034e+061624.842248209.500000
50%2007.071.9355001.051753e+076124.371109410.000000
75%2007.076.4132503.121004e+0718008.835640636.000000
max2007.082.6030001.318683e+0949357.190170894.000000
+ +
+
+
+
+
px.strip(df, x='lifeExp', hover_name="country")
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.strip(df, x='lifeExp', color="continent", hover_name="country")
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.histogram(df, x='lifeExp', color="continent", hover_name="country")
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.histogram(df, x='lifeExp', color="continent", hover_name="country", marginal="rug")
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.histogram(df, x='lifeExp', y="pop", color="continent", hover_name="country", marginal="rug")
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.histogram(df, x='lifeExp', y="pop", color="continent", hover_name="country", marginal="rug", facet_col="continent")
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.bar(df, color='lifeExp', x="pop", y="continent", hover_name="country")
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.sunburst(df, color='lifeExp', values="pop", path=["continent", "country"], hover_name="country", height=500)
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.treemap(df, color='lifeExp', values="pop", path=["continent", "country"], hover_name="country", height=500)
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.choropleth(df, color='lifeExp', locations="iso_alpha", hover_name="country", height=500)
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.scatter(df, x="gdpPercap", y='lifeExp', hover_name="country", height=500)
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.scatter(df, x="gdpPercap", y='lifeExp', hover_name="country", color="continent",size="pop", height=500)
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+

We can see that the curve follows a logarithmic path, so make log_x=True to straighten out the line to view the relationships in an easier manner. In the graph below we can view the monotic and nonmonotonic relationships in the dataset.

+
+
px.scatter(df, x="gdpPercap", y='lifeExp', hover_name="country", color="continent",size="pop", size_max=60, log_x=True, height=500)
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
fig = px.scatter(df, x="gdpPercap", y='lifeExp', hover_name="country", color="continent",size="pop", size_max=60, log_x=True, height=500)
+
+

This will allow you to inspect the values for each of these cells, unfortunately this is a great deal easier to see in JupyterLab.

+
+
fig.show("json")
+
+
Unable to display output for mime type(s): application/json
+
+
+
+
import plotly.express as px
+
+df = px.data.gapminder().query("year == 2007")
+
+fig = px.scatter(df, x="gdpPercap",y="lifeExp", color="continent", log_x=True, size="pop", size_max=60,
+                 hover_name="country", height=600, width=1000, template="simple_white", 
+                 color_discrete_sequence=px.colors.qualitative.G10,
+                 title="Health vs Wealth 2007",
+                 labels=dict(
+                     continent="Continent", pop="Population",
+                     gdpPercap="GDP per Capita (US$, price-adjusted)", 
+                     lifeExp="Life Expectancy (years)"))
+
+fig.update_layout(font_family="Rockwell",
+                  legend=dict(orientation="h", title="", y=1.1, x=1, xanchor="right", yanchor="bottom"))
+fig.update_xaxes(tickprefix="$", range=[2,5], dtick=1)
+fig.update_yaxes(range=[30,90])
+fig.add_hline((df["lifeExp"]*df["pop"]).sum()/df["pop"].sum(), line_width=1, line_dash="dot")
+fig.add_vline((df["gdpPercap"]*df["pop"]).sum()/df["pop"].sum(), line_width=1, line_dash="dot")
+fig.show()
+
+# fig.write_image("gapminder_2007.svg") # static export
+# fig.write_html("gapminder_2007.html") # interactive export
+# fig.write_json("gapminder_2007.json") # serialized export
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+

Animations in Plotly Express

+
+
df_animation = px.data.gapminder()
+
+anim_fig = px.scatter(df_animation, x="gdpPercap", y="lifeExp",
+                      title="Health vs Wealth from 1952 to 2007",
+                      labels=dict(continent="Continent", pop="Population", gdpPercap="GDP per Capita (US$, price-adjusted)", lifeExp="Life Expectancy (years)"),
+                      animation_frame="year", animation_group="country",
+                      size="pop",
+                      color="continent",
+                      hover_name="country",
+                      height=600,width=1000,
+                      template="simple_white",
+                      color_discrete_sequence=px.colors.qualitative.G10,
+                      log_x=True,
+                      size_max=60,
+                      range_x=[100,100000],
+                      range_y=[25,90])
+
+anim_fig.update_layout(font_family="Rockwell",
+                  legend=dict(orientation="h", title="", y=1.1, x=1, xanchor="right", yanchor="bottom"))
+anim_fig.update_xaxes(tickprefix="$", range=[2,5], dtick=1)
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
anim_fig.write_html("gapminder_animation.html", auto_play=False) # You're able to export this animation.
+
+
+
px.defaults.height=600
+
+
+
import plotly.express as px
+
+z = [[.1, .3, .5, .7, .9],
+     [1, .8, .6, .4, .2],
+     [.2, 0, .5, .7, .9],
+     [.9, .8, .4, .2, 0],
+     [.3, .4, .5, .7, 1]]
+
+fig = px.imshow(z, text_auto=True)
+fig.show()
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
import plotly.express as px
+df = px.data.wind()
+fig = px.bar_polar(df, r="frequency", theta="direction", height=600,
+                   color="strength", template="plotly_dark",
+                   color_discrete_sequence= px.colors.sequential.Plasma_r)
+fig.show()
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
df = px.data.iris()
+fig = px.parallel_coordinates(df, color="species_id", labels={"species_id": "Species",
+                  "sepal_width": "Sepal Width", "sepal_length": "Sepal Length",
+                  "petal_width": "Petal Width", "petal_length": "Petal Length", },
+                    color_continuous_scale=px.colors.diverging.Tealrose, color_continuous_midpoint=2)
+fig.show()
+
+
c:\Users\gpower\Anaconda3\envs\cary_dev\lib\site-packages\plotly\express\_core.py:279: FutureWarning:
+
+iteritems is deprecated and will be removed in a future version. Use .items instead.
+
+
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
df = px.data.tips()
+fig = px.parallel_categories(df, color="size", color_continuous_scale=px.colors.sequential.Inferno)
+fig.show()
+
+
c:\Users\gpower\Anaconda3\envs\cary_dev\lib\site-packages\plotly\express\_core.py:279: FutureWarning:
+
+iteritems is deprecated and will be removed in a future version. Use .items instead.
+
+
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
df = px.data.iris()
+fig = px.parallel_coordinates(df, color="species_id", labels={"species_id": "Species",
+                  "sepal_width": "Sepal Width", "sepal_length": "Sepal Length",
+                  "petal_width": "Petal Width", "petal_length": "Petal Length", },
+                    color_continuous_scale=px.colors.diverging.Tealrose, color_continuous_midpoint=2)
+fig.show()
+
+
c:\Users\gpower\Anaconda3\envs\cary_dev\lib\site-packages\plotly\express\_core.py:279: FutureWarning:
+
+iteritems is deprecated and will be removed in a future version. Use .items instead.
+
+
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
df = px.data.tips()
+fig = px.parallel_categories(df, color="size", color_continuous_scale=px.colors.sequential.Inferno)
+fig.show()
+
+
c:\Users\gpower\Anaconda3\envs\cary_dev\lib\site-packages\plotly\express\_core.py:279: FutureWarning:
+
+iteritems is deprecated and will be removed in a future version. Use .items instead.
+
+
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
df = px.data.election()
+fig = px.scatter_ternary(df, a="Joly", b="Coderre", c="Bergeron", color="winner", size="total", hover_name="district",
+                   size_max=15, color_discrete_map = {"Joly": "blue", "Bergeron": "green", "Coderre":"red"} )
+fig.show()
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
df = px.data.election()
+fig = px.scatter_3d(df, x="Joly", y="Coderre", z="Bergeron", color="winner", size="total", hover_name="district",
+                  symbol="result", color_discrete_map = {"Joly": "blue", "Bergeron": "green", "Coderre":"red"})
+fig.show()
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+ + +
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/introducing_matplotlib.html b/_site/notebooks/introducing_matplotlib.html new file mode 100644 index 0000000..577043e --- /dev/null +++ b/_site/notebooks/introducing_matplotlib.html @@ -0,0 +1,1010 @@ + + + + + + + + + +Cary Introduction to Python - Getting Started with Matplotlib + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ +
+
+

Getting Started with Matplotlib

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Getting Started with Matplotlib

+

Pandas uses matplotlib to create visualizations. Therefore, before we learn how to plot with pandas, it’s important to understand how matplotlib works at a high-level, which is the focus of this notebook.

+
+

About the Data

+

In this notebook, we will be working with 2 datasets: - Facebook’s stock price throughout 2018 (obtained using the stock_analysis package) - Earthquake data from September 18, 2018 - October 13, 2018 (obtained from the US Geological Survey (USGS) using the USGS API)

+
+
+

Setup

+

We need to import matplotlib.pyplot for plotting.

+
+
import matplotlib.pyplot as plt
+import pandas as pd
+
+
+
+

Plotting lines

+
+
fb = pd.read_csv(
+    '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True
+)
+
+plt.plot(fb.index, fb.open)
+plt.show()
+
+
+
+

+
+
+
+
+

Since we are working in a Jupyter notebook, we can use the magic command %matplotlib inline once and not have to call plt.show() for each plot.

+
+
%matplotlib inline
+import matplotlib.pyplot as plt
+import pandas as pd
+
+fb = pd.read_csv(
+    '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True
+)
+plt.plot(fb.index, fb.open)
+
+
+
+

+
+
+
+
+
+
+

Scatter plots

+

We can pass in a string specifying the style of the plot. This is of the form [marker][linestyle][color]. For example, we can make a black dashed line with '--k' or a red scatter plot with 'or':

+
+
plt.plot('high', 'low', 'or', data=fb.head(20))
+
+
+
+

+
+
+
+
+

Here are some examples of how you make a format string:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MarkerLinestyleColorFormat StringResult
-b-bblue solid line
.k.kblack points
--r--rred dashed line
o-go-ggreen solid line with circles
:m:mmagenta dotted line
x-.cx-.ccyan dot-dashed line with x’s
+

Note that we can also use format strings of the form [color][marker][linestyle], but the parsing by matplotlib (in rare cases) might not be what we were aiming for. Consult the Notes section in the documentation for the complete list of options. ## Histograms

+
+
quakes = pd.read_csv('../data/earthquakes.csv')
+plt.hist(quakes.query('magType == "ml"').mag)
+
+
(array([6.400e+01, 4.450e+02, 1.137e+03, 1.853e+03, 2.114e+03, 8.070e+02,
+        2.800e+02, 9.200e+01, 9.000e+00, 2.000e+00]),
+ array([-1.26 , -0.624,  0.012,  0.648,  1.284,  1.92 ,  2.556,  3.192,
+         3.828,  4.464,  5.1  ]),
+ <BarContainer object of 10 artists>)
+
+
+
+
+

+
+
+
+
+
+

Bin size matters

+

Notice how our assumptions of the distribution of the data can change based on the number of bins (look at the drop between the two highest peaks on the righthand plot):

+
+
x = quakes.query('magType == "ml"').mag
+fig, axes = plt.subplots(1, 2, figsize=(10, 3))
+for ax, bins in zip(axes, [7, 35]):
+    ax.hist(x, bins=bins)
+    ax.set_title(f'bins param: {bins}')
+
+
+
+

+
+
+
+
+
+
+
+

Plot components

+
+

Figure

+

Top-level object that holds the other plot components.

+
+
fig = plt.figure()
+
+
<Figure size 640x480 with 0 Axes>
+
+
+
+
+

Axes

+

Individual plots contained within the Figure.

+
+
+
+

Creating subplots

+

Simply specify the number of rows and columns to create:

+
+
fig, axes = plt.subplots(1, 2)
+
+
+
+

+
+
+
+
+

As an alternative to using plt.subplots() we can add Axes objects to the Figure object on our own. This allows for some more complex layouts, such as picture in picture:

+
+
fig = plt.figure(figsize=(3, 3))
+outside = fig.add_axes([0.1, 0.1, 0.9, 0.9])
+inside = fig.add_axes([0.7, 0.7, 0.25, 0.25])
+
+
+
+

+
+
+
+
+
+
+

Creating Plot Layouts with gridspec

+

We can create subplots with varying sizes as well:

+
+
fig = plt.figure(figsize=(8, 8))
+gs = fig.add_gridspec(3, 3)
+top_left = fig.add_subplot(gs[0, 0])
+mid_left = fig.add_subplot(gs[1, 0])
+top_right = fig.add_subplot(gs[:2, 1:])
+bottom = fig.add_subplot(gs[2,:])
+
+
+
+

+
+
+
+
+
+
+

Saving plots

+

Use plt.savefig() to save the last created plot. To save a specific Figure object, use its savefig() method. Which supports ‘png’, ‘pdf’, ‘svg’, and ‘eps’ filetypes.

+
+
fig.savefig('empty.png')
+fig.savefig('empty.pdf')
+fig.savefig('empty.svg')
+fig.savefig('empty.eps')
+
+
+
+

Cleaning up

+

It’s important to close resources when we are done with them. We use plt.close() to do so. If we pass in nothing, it will close the last plot, but we can pass in the specific Figure object to close or say 'all' to close all Figure objects that are open. Let’s close all the Figure objects that are open with plt.close():

+
+
plt.close('all')
+
+
+
+

Additional plotting options

+
+

Specifying figure size

+

Just pass the figsize argument to plt.figure(). It’s a tuple of (width, height):

+
+
fig = plt.figure(figsize=(10, 4))
+
+
<Figure size 1000x400 with 0 Axes>
+
+
+

This can be specified when creating subplots as well:

+
+
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
+
+
+
+

+
+
+
+
+
+
+

rcParams

+

A small subset of all the available plot settings (shuffling to get a good variation of options):

+
+
import random
+import matplotlib as mpl
+
+rcparams_list = list(mpl.rcParams.keys())
+random.seed(20) # make this repeatable
+random.shuffle(rcparams_list)
+sorted(rcparams_list[:20])
+
+
['animation.convert_args',
+ 'axes.edgecolor',
+ 'axes.formatter.use_locale',
+ 'axes.spines.right',
+ 'boxplot.meanprops.markersize',
+ 'boxplot.showfliers',
+ 'keymap.home',
+ 'lines.markerfacecolor',
+ 'lines.scale_dashes',
+ 'mathtext.rm',
+ 'patch.force_edgecolor',
+ 'savefig.facecolor',
+ 'svg.fonttype',
+ 'text.hinting_factor',
+ 'xtick.alignment',
+ 'xtick.minor.top',
+ 'xtick.minor.width',
+ 'ytick.left',
+ 'ytick.major.left',
+ 'ytick.minor.width']
+
+
+

We can check the current default figsize using rcParams:

+
+
mpl.rcParams['figure.figsize']
+
+
[6.4, 4.8]
+
+
+

We can also update this value to change the default (until the kernel is restarted):

+
+
mpl.rcParams['figure.figsize'] = (300, 10)
+mpl.rcParams['figure.figsize']
+
+
[300.0, 10.0]
+
+
+

Use rcdefaults() to restore the defaults. Note this is slightly different than before because running %matplotlib inline sets a different value for figsize (see more). After we reset, we are going back to the default value of figsize before that import:

+
+
mpl.rcdefaults()
+mpl.rcParams['figure.figsize']
+
+
[6.4, 4.8]
+
+
+

This can also be done via pyplot:

+
+
plt.rc('figure', figsize=(20, 20)) # change `figsize` default to (20, 20)
+plt.rcdefaults() # reset the default
+
+ + +
+
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/introducing_matplotlib_files/figure-html/cell-10-output-1.png b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-10-output-1.png new file mode 100644 index 0000000..22f358a Binary files /dev/null and b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-10-output-1.png differ diff --git a/_site/notebooks/introducing_matplotlib_files/figure-html/cell-11-output-1.png b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-11-output-1.png new file mode 100644 index 0000000..13f4aa6 Binary files /dev/null and b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-11-output-1.png differ diff --git a/_site/notebooks/introducing_matplotlib_files/figure-html/cell-15-output-1.png b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-15-output-1.png new file mode 100644 index 0000000..1c91b7a Binary files /dev/null and b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-15-output-1.png differ diff --git a/_site/notebooks/introducing_matplotlib_files/figure-html/cell-3-output-1.png b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-3-output-1.png new file mode 100644 index 0000000..eb03fb8 Binary files /dev/null and b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-3-output-1.png differ diff --git a/_site/notebooks/introducing_matplotlib_files/figure-html/cell-4-output-1.png b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-4-output-1.png new file mode 100644 index 0000000..eb03fb8 Binary files /dev/null and b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-4-output-1.png differ diff --git a/_site/notebooks/introducing_matplotlib_files/figure-html/cell-5-output-1.png b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-5-output-1.png new file mode 100644 index 0000000..77304c0 Binary files /dev/null and b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-5-output-1.png differ diff --git a/_site/notebooks/introducing_matplotlib_files/figure-html/cell-6-output-2.png b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-6-output-2.png new file mode 100644 index 0000000..df8cdf0 Binary files /dev/null and b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-6-output-2.png differ diff --git a/_site/notebooks/introducing_matplotlib_files/figure-html/cell-7-output-1.png b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-7-output-1.png new file mode 100644 index 0000000..f3ec1f7 Binary files /dev/null and b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-7-output-1.png differ diff --git a/_site/notebooks/introducing_matplotlib_files/figure-html/cell-9-output-1.png b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-9-output-1.png new file mode 100644 index 0000000..1540129 Binary files /dev/null and b/_site/notebooks/introducing_matplotlib_files/figure-html/cell-9-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis.html b/_site/notebooks/introduction_to_data_analysis.html new file mode 100644 index 0000000..6be3a90 --- /dev/null +++ b/_site/notebooks/introduction_to_data_analysis.html @@ -0,0 +1,1052 @@ + + + + + + + + + +Cary Introduction to Python - Introduction to Data Analysis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ +
+
+

Introduction to Data Analysis

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Introduction to Data Analysis

+

This notebook serves as a summary of the fundamentals. For a Python crash-course/refresher, work through the python_101.ipynb notebook.

+
+

Setup

+
+
import sys
+sys.path.append('../src/')
+sys.dont_write_bytecode = True
+
+import stats_viz
+
+
+
+

Fundamentals of data analysis

+

When conducting a data analysis, we will move back and forth between four main processes:

+
    +
  • Data Collection: Every analysis starts with collecting data. We can collect data from a variety of sources, including databases, APIs, flat files, and the Internet.
  • +
  • Data Wrangling: After we have our data, we need to prepare it for our analysis. This may involve reshaping it, changing data types, handling missing values, and/or aggregating it.
  • +
  • Exploratory Data Analysis (EDA): We can use visualizations to explore our data and summarize it. During this time, we will also begin exploring the data by looking at its structure, format, and summary statistics.
  • +
  • Drawing Conclusions: After we have thoroughly explored our data, we can try to draw conclusions or model it.
  • +
+
+
+

Statistical Foundations

+

As this is an overview of statistics, we will discuss some concepts. By no means is this exhaustive.

+
+

Sampling

+

Some resampling (sampling from the sample) techniques you will see: - simple random sampling: pick with a random number generator - stratified random sampling: randomly pick preserving the proportion of groups in the data - bootstrapping: sampling with replacement (more info: YouTube video and Wikipedia article)

+
+
+

Descriptive Statistics

+

We use descriptive statistics to describe the data. The data we work with is usually a sample taken from the population. The statistics we will discuss here are referred to as sample statistics because they are calculated on the sample and can be used as estimators for the population parameters.

+
+

Measures of Center

+

Three common ways to describe the central tendency of a distribution are mean, median, and mode. ##### Mean The sample mean is an estimator for the population mean (\(\mu\)) and is defined as:

+

\[\bar{x} = \frac{\sum_{1}^{n} x_i}{n}\] ##### Median The median represents the 50th percentile of our data; this means that 50% of the values are greater than the median and 50% are less than the median. It is calculated by taking the middle value from an ordered list of values.

+
+
Mode
+

The mode is the most common value in the data. We can use it to describe categorical data or, for continuous data, the shape of the distribution:

+
+
ax = stats_viz.different_modal_plots()
+
+
+
+

+
+
+
+
+
+
+
+

Measures of Spread

+

Measures of spread tell us how the data is dispersed; this will indicate how thin (low dispersion) or wide (very spread out) our distribution is.

+
+
Range
+

The range is the distance between the smallest value (minimum) and the largest value (maximum):

+

\[range = max(X) - min(X)\]

+
+
+
Variance
+

The variance describes how far apart observations are spread out from their average value (the mean). When calculating the sample variance, we divide by n - 1 instead of n to account for using the sample mean (\(\bar{x}\)):

+

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

+

This is referred to as Bessel’s correction and is applied to get an unbiased estimator of the population variance.

+

Note that this will be in units-squared of whatever was being measured.

+
+
+
Standard Deviation
+

The standard deviation is the square root of the variance, giving us a measure in the same units as our data. The sample standard deviation is calculated as follows:

+

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

+
+
ax = stats_viz.effect_of_std_dev()
+
+
+
+

+
+
+
+
+

Note that \(\sigma^2\) is the population variance and \(\sigma\) is the population standard deviation.

+
+
+
Coefficient of Variation
+

The coefficient of variation (CV) gives us a unitless ratio of the standard deviation to the mean. Since, it has no units we can compare dispersion across datasets:

+

\[CV = \frac{s}{\bar{x}}\]

+
+
+
Interquartile Range
+

The interquartile range (IQR) gives us the spread of data around the median and quantifies how much dispersion we have in the middle 50% of our distribution:

+

\[IQR = Q_3 - Q_1\]

+
+
+
Quartile Coefficient of Dispersion
+

The quartile coefficient of dispersion also is a unitless statistic for comparing datasets. However, it uses the median as the measure of center. It is calculated by dividing the semi-quartile range (half the IQR) by the midhinge (midpoint between the first and third quartiles):

+

\[QCD = \frac{\frac{Q_3 - Q_1}{2}}{\frac{Q_1 + Q_3}{2}} = \frac{Q_3 - Q_1}{Q_3 + Q_1}\]

+
+
+
+

Summarizing data

+

The 5-number summary provides 5 descriptive statistics that summarize our data:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
QuartileStatisticPercentile
1.\(Q_0\)minimum\(0^{th}\)
2.\(Q_1\)N/A\(25^{th}\)
3.\(Q_2\)median\(50^{th}\)
4.\(Q_3\)N/A\(75^{th}\)
5.\(Q_4\)maximum\(100^{th}\)
+

This summary can be visualized using a box plot (also called box-and-whisker plot). The box has an upper bound of \(Q_3\) and a lower bound of \(Q_1\). The median will be a line somewhere in this box. The whiskers extend from the box towards the minimum/maximum. For our purposes, they will extend to \(Q_3 + 1.5 \times IQR\) and \(Q_1 - 1.5 \times IQR\) and anything beyond will be represented as individual points for outliers:

+
+
ax = stats_viz.example_boxplot()
+
+
+
+

+
+
+
+
+

The box plot doesn’t show us how the data is distributed within the quartiles. To get a better sense of the distribution, we can use a histogram, which will show us the amount of observations that fall into equal-width bins. We can vary the number of bins to use, but be aware that this can change our impression of what the distribution appears to be:

+
+
ax = stats_viz.example_histogram()
+
+
+
+

+
+
+
+
+

We can also visualize the distribution using a kernel density estimate (KDE). This will estimate the probability density function (PDF). This function shows how probability is distributed over the values. Higher values of the PDF mean higher likelihoods:

+
+
ax = stats_viz.example_kde()
+
+
+
+

+
+
+
+
+

Note that both the KDE and histogram estimate the distribution:

+
+
ax = stats_viz.hist_and_kde()
+
+
+
+

+
+
+
+
+

Skewed distributions have more observations on one side. The mean will be less than the median with negative skew, while the opposite is true of positive skew:

+
+
ax = stats_viz.skew_examples()
+
+
+
+

+
+
+
+
+

We can use the cumulative distribution function (CDF) to find probabilities of getting values within a certain range. The CDF is the integral of the PDF:

+

\[CDF = F(x) = \int_{-\infty}^{x} f(t) dt\]

+

Note that \(f(t)\) is the PDF and \(\int_{-\infty}^{\infty} f(t) dt = 1\).

+

The probability of the random variable \(X\) being less than or equal to the specific value of \(x\) is denoted as \(P(X ≤ x)\). Note that for a continuous random variable the probability of it being exactly \(x\) is zero.

+

Let’s look at the estimate of the CDF from the sample data we used for the box plot, called the empirical cumulative distribution function (ECDF):

+
+
ax = stats_viz.cdf_example()
+
+
+
+

+
+
+
+
+

We can find any range we want if we use some algebra as in the rightmost subplot above.

+
+
+

Common Distributions

+
    +
  • Gaussian (normal) distribution: looks like a bell curve and is parameterized by its mean (μ) and standard deviation (σ). Many things in nature happen to follow the normal distribution, like heights. Note that testing if a distribution is normal is not trivial. Written as \(N(\mu, \sigma)\).
  • +
  • Poisson distribution: discrete distribution that is often used to model arrivals. Parameterized by its mean, lambda (λ). Written as \(Pois(\lambda)\).
  • +
  • Exponential distribution: can be used to model the time between arrivals. Parameterized by its mean, lambda (λ). Written as \(Exp(\lambda)\).
  • +
  • Uniform distribution: places equal likelihood on each value within its bounds (a and b). We often use this for random number generation. Written as \(U(a, b)\).
  • +
  • Bernoulli distribution: When we pick a random number to simulate a single success/failure outcome, it is called a Bernoulli trial. This is parameterized by the probability of success (p). Written as \(Bernoulli(p)\).
  • +
  • Binomial distribution: When we run the same experiment n times, the total number of successes is then a binomial random variable. Written as \(B(n, p)\).
  • +
+

We can visualize both discrete and continuous distributions; however, discrete distributions give us a probability mass function (PMF) instead of a PDF:

+
+
ax = stats_viz.common_dists()
+
+
+
+

+
+
+
+
+
+
+

Scaling data

+

In order to compare variables from different distributions, we would have to scale the data, which we could do with the range by using min-max scaling:

+

\[x_{scaled}=\frac{x - min(X)}{range(X)}\]

+

Another way is to use a Z-score to standardize the data:

+

\[z_i = \frac{x_i - \bar{x}}{s}\]

+
+
+

Quantifying relationships between variables

+

The covariance is a statistic for quantifying the relationship between variables by showing how one variable changes with respect to another (also referred to as their joint variance):

+

\[cov(X, Y) = E[(X-E[X])(Y-E[Y])]\]

+

E[X] is the expectation of the random variable X (its long-run average).

+

The sign of the covariance gives us the direction of the relationship, but we need the magnitude as well. For that, we calculate the Pearson correlation coefficient (\(\rho\)):

+

\[\rho_{X, Y} = \frac{cov(X, Y)}{s_X s_Y}\]

+

Examples:

+
+
ax = stats_viz.correlation_coefficient_examples()
+
+
+
+

+
+
+
+
+

From left to right: no correlation, weak negative correlation, strong positive correlation, and nearly perfect negative correlation.

+

Often, it is more informative to use scatter plots to check for relationships between variables. This is because the correlation may be strong, but the relationship may not be linear:

+
+
ax = stats_viz.non_linear_relationships()
+
+
+
+

+
+
+
+
+

Remember, correlation does not imply causation. While we may find a correlation between X and Y, it does not mean that X causes Y or Y causes X. It is possible there is some Z that causes both or that X causes some intermediary event that causes Y — it could even be a coincidence. Be sure to check out Tyler Vigen’s Spurious Correlations blog for some interesting correlations.

+
+
+

Pitfalls of summary statistics

+

Not only can our correlation coefficients be misleading, but so can summary statistics. Anscombe’s quartet is a collection of four different datasets that have identical summary statistics and correlation coefficients, however, when plotted, it is obvious they are not similar:

+
+
ax = stats_viz.anscombes_quartet()
+
+
+
+

+
+
+
+
+

Another example of this is the Datasaurus Dozen:

+
+
ax = stats_viz.datasaurus_dozen()
+
+
+
+

+
+
+
+
+
+
+
+

Prediction and forecasting

+

Say our favorite ice cream shop has asked us to help predict how many ice creams they can expect to sell on a given day. They are convinced that the temperature outside has strong influence on their sales, so they collected data on the number of ice creams sold at a given temperature. We agree to help them, and the first thing we do is make a scatter plot of the data they gave us:

+
+
ax = stats_viz.example_scatter_plot()
+
+
+
+

+
+
+
+
+

We can observe an upward trend in the scatter plot: more ice creams are sold at higher temperatures. In order to help out the ice cream shop, though, we need to find a way to make predictions from this data. We can use a technique called regression to model the relationship between temperature and ice cream sales with an equation:

+
+
ax = stats_viz.example_regression()
+
+
+
+

+
+
+
+
+

We can use the resulting equation to make predictions for the number of ice creams sold at various temperatures. However, we must keep in mind if we are interpolating or extrapolating. If the temperature value we are using for prediction is within the range of the original data we used to build our regression model, then we are interpolating (solid portion of the red line). On the other hand, if the temperature is beyond the values in the original data, we are extrapolating, which is very dangerous, since we can’t assume the pattern continues indefinitely in each direction (dotted portion of the line). Extremely hot temperatures may cause people to stay inside, meaning no ice creams will be sold, while the equation indicates record-high sales.

+

Forecasting is a type of prediction for time series. In a process called time series decomposition, time series is decomposed into a trend component, a seasonality component, and a cyclical component. These components can be combined in an additive or multiplicative fashion:

+
+
ax = stats_viz.time_series_decomposition_example()
+
+
+
+

+
+
+
+
+

The trend component describes the behavior of the time series in the long-term without accounting for the seasonal or cyclical effects. Using the trend, we can make broad statements about the time series in the long-run, such as: the population of Earth is increasing or the value of a stock is stagnating. Seasonality of a time series explains the systematic and calendar-related movements of a time series. For example, the number of ice cream trucks on the streets of New York City is high in the summer and drops to nothing in the winter; this pattern repeats every year regardless of whether the actual amount each summer is the same. Lastly, the cyclical component accounts for anything else unexplained or irregular with the time series; this could be something like a hurricane driving the number of ice cream trucks down in the short-term because it isn’t safe to be outside. This component is difficult to anticipate with a forecast due to its unexpected nature.

+

When making models to forecast time series, some common methods include ARIMA-family methods and exponential smoothing. ARIMA stands for autoregressive (AR), integrated (I), moving average (MA). Autoregressive models take advantage of the fact that an observation at time \(t\) is correlated to a previous observation, for example at time \(t - 1\). Note that not all time series are autoregressive. The integrated component concerns the differenced data, or the change in the data from one time to another. Lastly, the moving average component uses a sliding window to average the last \(x\) observations where \(x\) is the length of the sliding window.

+

The moving average puts equal weight on each time period in the past involved in the calculation. In practice, this isn’t always a realistic expectation of our data. Sometimes all past values are important, but they vary in their influence on future data points. For these cases, we can use exponential smoothing, which allows us to put more weight on more recent values and less weight on values further away from what we are predicting.

+
+
+

Inferential Statistics

+

Inferential statistics deals with inferring or deducing things from the sample data we have in order to make statements about the population as a whole. Before doing so, we need to know whether we conducted an observational study or an experiment. An observational study can’t be used to determine causation because we can’t control for everything. An experiment on the other hand is controlled.

+

Remember that the sample statistics we discussed earlier are estimators for the population parameters. Our estimators need confidence intervals, which provide a point estimate and a margin of error around it. This is the range that the true population parameter will be in at a certain confidence level. At the 95% confidence level, 95% of the confidence intervals calculated from random samples of the population contain the true population parameter.

+

We also have the option of using hypothesis testing. First, we define a null hypothesis (say the true population mean is 0), then we determine a significance level (1 - confidence level), which is the probability of rejecting the null hypothesis when it is true. Our result is statistically significant if the value for the null hypothesis is outside the confidence interval. More info.

+ + +
+
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-10-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-10-output-1.png new file mode 100644 index 0000000..b88bf8f Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-10-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-11-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-11-output-1.png new file mode 100644 index 0000000..5ecea84 Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-11-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-12-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-12-output-1.png new file mode 100644 index 0000000..43cfda1 Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-12-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-13-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-13-output-1.png new file mode 100644 index 0000000..960a92f Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-13-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-14-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-14-output-1.png new file mode 100644 index 0000000..d27f574 Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-14-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-15-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-15-output-1.png new file mode 100644 index 0000000..ea51cd9 Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-15-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-16-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-16-output-1.png new file mode 100644 index 0000000..c86a3f1 Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-16-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-17-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-17-output-1.png new file mode 100644 index 0000000..0c7875b Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-17-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-18-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-18-output-1.png new file mode 100644 index 0000000..bfffa46 Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-18-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-3-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-3-output-1.png new file mode 100644 index 0000000..24236e5 Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-3-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-4-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-4-output-1.png new file mode 100644 index 0000000..73da7f6 Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-4-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-5-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-5-output-1.png new file mode 100644 index 0000000..fd220d2 Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-5-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-6-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-6-output-1.png new file mode 100644 index 0000000..9fcc164 Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-6-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-7-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-7-output-1.png new file mode 100644 index 0000000..aea5b49 Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-7-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-8-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-8-output-1.png new file mode 100644 index 0000000..8e7f355 Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-8-output-1.png differ diff --git a/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-9-output-1.png b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-9-output-1.png new file mode 100644 index 0000000..30a503b Binary files /dev/null and b/_site/notebooks/introduction_to_data_analysis_files/figure-html/cell-9-output-1.png differ diff --git a/_site/notebooks/making_dataframes_from_api_requests.html b/_site/notebooks/making_dataframes_from_api_requests.html new file mode 100644 index 0000000..345bb69 --- /dev/null +++ b/_site/notebooks/making_dataframes_from_api_requests.html @@ -0,0 +1,939 @@ + + + + + + + + + +Cary Introduction to Python - Making Pandas DataFrames from API Requests + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ +
+
+

Making Pandas DataFrames from API Requests

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Making Pandas DataFrames from API Requests

+

In this example, we will use the U.S. Geological Survey’s API to grab a JSON object of earthquake data and convert it to a pandas.DataFrame.

+

USGS API: https://earthquake.usgs.gov/fdsnws/event/1/

+
+

Get Data from API

+
+
import datetime as dt
+import pandas as pd
+import requests
+
+yesterday = dt.date.today() - dt.timedelta(days=1)
+api = 'https://earthquake.usgs.gov/fdsnws/event/1/query'
+payload = {
+    'format': 'geojson',
+    'starttime': yesterday - dt.timedelta(days=30),
+    'endtime': yesterday
+}
+response = requests.get(api, params=payload)
+
+# let's make sure the request was OK
+response.status_code
+
+
200
+
+
+

Response of 200 means OK, so we can pull the data out of the result. Since we asked the API for a JSON payload, we can extract it from the response with the json() method.

+
+
+

Isolate the Data from the JSON Response

+

We need to check the structures of the response data to know where our data is.

+
+
earthquake_json = response.json()
+earthquake_json.keys()
+
+
dict_keys(['type', 'metadata', 'features', 'bbox'])
+
+
+

The USGS API provides information about our request in the metadata key. Note that your result will be different, regardless of the date range you chose, because the API includes a timestamp for when the data was pulled:

+
+
earthquake_json['metadata']
+
+
{'generated': 1686247399000,
+ 'url': 'https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2023-05-08&endtime=2023-06-07',
+ 'title': 'USGS Earthquakes',
+ 'status': 200,
+ 'api': '1.14.0',
+ 'count': 11706}
+
+
+

Each element in the JSON array features is a row of data for our dataframe.

+
+
type(earthquake_json['features'])
+
+
list
+
+
+

Your data will be different depending on the date you run this.

+
+
earthquake_json['features'][0]
+
+
{'type': 'Feature',
+ 'properties': {'mag': 0.5,
+  'place': '17km SE of Anza, CA',
+  'time': 1686095356120,
+  'updated': 1686178472172,
+  'tz': None,
+  'url': 'https://earthquake.usgs.gov/earthquakes/eventpage/ci40479680',
+  'detail': 'https://earthquake.usgs.gov/fdsnws/event/1/query?eventid=ci40479680&format=geojson',
+  'felt': None,
+  'cdi': None,
+  'mmi': None,
+  'alert': None,
+  'status': 'reviewed',
+  'tsunami': 0,
+  'sig': 4,
+  'net': 'ci',
+  'code': '40479680',
+  'ids': ',ci40479680,',
+  'sources': ',ci,',
+  'types': ',nearby-cities,origin,phase-data,scitech-link,',
+  'nst': 21,
+  'dmin': 0.07016,
+  'rms': 0.26,
+  'gap': 86,
+  'magType': 'ml',
+  'type': 'earthquake',
+  'title': 'M 0.5 - 17km SE of Anza, CA'},
+ 'geometry': {'type': 'Point', 'coordinates': [-116.5405, 33.447, 11.37]},
+ 'id': 'ci40479680'}
+
+
+
+
+

Convert to DataFrame

+

We need to grab the properties section out of every entry in the features JSON array to create our dataframe.

+
+
earthquake_properties_data = [
+    quake['properties'] for quake in earthquake_json['features']
+]
+df = pd.DataFrame(earthquake_properties_data)
+df.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
magplacetimeupdatedtzurldetailfeltcdimmi...idssourcestypesnstdminrmsgapmagTypetypetitle
00.5017km SE of Anza, CA16860953561201686178472172Nonehttps://earthquake.usgs.gov/earthquakes/eventp...https://earthquake.usgs.gov/fdsnws/event/1/que...NaNNaNNaN...,ci40479680,,ci,,nearby-cities,origin,phase-data,scitech-link,21.00.0701600.2686.0mlearthquakeM 0.5 - 17km SE of Anza, CA
11.246 km W of Blanchard, Oklahoma16860952889001686140204481Nonehttps://earthquake.usgs.gov/earthquakes/eventp...https://earthquake.usgs.gov/fdsnws/event/1/que...0.01.0NaN...,ok2023lavp,,ok,,dyfi,origin,phase-data,64.00.0890830.4442.0mlearthquakeM 1.2 - 6 km W of Blanchard, Oklahoma
25.20southeast of the Loyalty Islands16860946935721686095577040Nonehttps://earthquake.usgs.gov/earthquakes/eventp...https://earthquake.usgs.gov/fdsnws/event/1/que...NaNNaNNaN...,us7000k6rg,,us,,origin,phase-data,32.02.7550000.8285.0mbearthquakeM 5.2 - southeast of the Loyalty Islands
30.2410 km NNE of Government Camp, Oregon16860946115901686118209350Nonehttps://earthquake.usgs.gov/earthquakes/eventp...https://earthquake.usgs.gov/fdsnws/event/1/que...NaNNaNNaN...,uw61931036,,uw,,origin,phase-data,3.00.0326200.04239.0mlearthquakeM 0.2 - 10 km NNE of Government Camp, Oregon
4-0.018 km NNE of Government Camp, Oregon16860941173101686117953180Nonehttps://earthquake.usgs.gov/earthquakes/eventp...https://earthquake.usgs.gov/fdsnws/event/1/que...NaNNaNNaN...,uw61931031,,uw,,origin,phase-data,4.00.0115100.04173.0mlearthquakeM 0.0 - 8 km NNE of Government Camp, Oregon
+ +

5 rows × 26 columns

+
+
+
+
+
+

(Optional) Write Data to CSV

+
+
df.to_csv('earthquakes.csv', index=False)
+
+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/one_hot_encoding.html b/_site/notebooks/one_hot_encoding.html new file mode 100644 index 0000000..7848464 --- /dev/null +++ b/_site/notebooks/one_hot_encoding.html @@ -0,0 +1,2325 @@ + + + + + + + + + +Cary Introduction to Python - One-Hot Encoding + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ +
+
+

One-Hot Encoding

+
+ + + +
+ + + + +
+ + + +
+ + +
+

One-Hot Encoding

+
+

Why Bother with One-Hot Encoding?

+

It’s useful for feeding categorical data into machine-learning algorithms since integers are computationally less expensive than strings.

+
+
import pandas as pd
+print(pd.__version__)
+
+
2.0.3
+
+
+
+
disengagements = pd.read_excel("../data/cassi-autonomous-shuttle/autonomous_shuttle_disengagement.xlsx",usecols=["Incident Datetime", "Location","Weather","Vehicle Speed in Miles per Hour", "Initiated by","Cause"], parse_dates=True)
+disengagements
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Incident DatetimeLocationWeatherVehicle Speed in Miles per HourInitiated byCause
02023-03-07T10:00:00-05:0035.7849964, -78.8268094Sunny;2OperatorFault Code/Error Code
12023-03-07T14:00:00-05:0035.7847312, -78.8245051Sunny;5OperatorStation Blocked
22023-03-07T14:30:00-05:0035.7824658, -78.8244159Sunny;5OperatorStation Blocked
32023-03-07T15:15:00-05:0035.7824658, -78.8244159Sunny;4OperatorStation Blocked
42023-03-08T10:00:00-05:0035.7852558, -78.8273737Sunny;2OperatorShuttle Manually Deviated from Approved Path
.....................
1742023-06-01T16:00:00-04:0035.783456, -78.821639Sunny;5OperatorSignal Loss
1752023-06-02T10:32:00-04:0035.7819145, -78.8235603Sunny;4OperatorStation Blocked
1762023-06-02T10:35:00-04:0035.7813188, -78.8256601Sunny;3OperatorStation Blocked
1772023-06-02T10:44:00-04:0035.7847325, -78.824496Sunny;4OperatorObstacle Detection
1782023-06-02T11:01:00-04:0035.7841086, -78.8261962Sunny;3OperatorSignalized Intersection
+ +

179 rows × 6 columns

+
+
+
+
+
disengagements.dtypes
+
+
Incident Datetime                  object
+Location                           object
+Weather                            object
+Vehicle Speed in Miles per Hour     int64
+Initiated by                       object
+Cause                              object
+dtype: object
+
+
+
+
disengagements['Incident Datetime'] = pd.to_datetime(disengagements['Incident Datetime'], utc=True)
+disengagements['Initiated by'] = disengagements['Initiated by'].astype('category')
+disengagements['Cause'] = disengagements['Cause'].astype('category')
+disengagements.dtypes
+
+
Incident Datetime                  datetime64[ns, UTC]
+Location                                        object
+Weather                                         object
+Vehicle Speed in Miles per Hour                  int64
+Initiated by                                  category
+Cause                                         category
+dtype: object
+
+
+
+
disengagements = disengagements.assign(week_of_year = disengagements['Incident Datetime'].dt.isocalendar().week, week_of_pilot = lambda x: disengagements['Incident Datetime'].dt.isocalendar().week - 9)
+disengagements
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Incident DatetimeLocationWeatherVehicle Speed in Miles per HourInitiated byCauseweek_of_yearweek_of_pilot
02023-03-07 15:00:00+00:0035.7849964, -78.8268094Sunny;2OperatorFault Code/Error Code101
12023-03-07 19:00:00+00:0035.7847312, -78.8245051Sunny;5OperatorStation Blocked101
22023-03-07 19:30:00+00:0035.7824658, -78.8244159Sunny;5OperatorStation Blocked101
32023-03-07 20:15:00+00:0035.7824658, -78.8244159Sunny;4OperatorStation Blocked101
42023-03-08 15:00:00+00:0035.7852558, -78.8273737Sunny;2OperatorShuttle Manually Deviated from Approved Path101
...........................
1742023-06-01 20:00:00+00:0035.783456, -78.821639Sunny;5OperatorSignal Loss2213
1752023-06-02 14:32:00+00:0035.7819145, -78.8235603Sunny;4OperatorStation Blocked2213
1762023-06-02 14:35:00+00:0035.7813188, -78.8256601Sunny;3OperatorStation Blocked2213
1772023-06-02 14:44:00+00:0035.7847325, -78.824496Sunny;4OperatorObstacle Detection2213
1782023-06-02 15:01:00+00:0035.7841086, -78.8261962Sunny;3OperatorSignalized Intersection2213
+ +

179 rows × 8 columns

+
+
+
+
+
disengagements['Cause']
+
+
0                             Fault Code/Error Code
+1                                   Station Blocked
+2                                   Station Blocked
+3                                   Station Blocked
+4      Shuttle Manually Deviated from Approved Path
+                           ...                     
+174                                     Signal Loss
+175                                 Station Blocked
+176                                 Station Blocked
+177                              Obstacle Detection
+178                         Signalized Intersection
+Name: Cause, Length: 179, dtype: category
+Categories (9, object): ['Fault Code/Error Code', 'Obstacle Detection', 'Other Road Users', 'Priority Zone', ..., 'Signal Loss', 'Signalized Intersection', 'Station Blocked', 'Vegetation']
+
+
+
+
disengagements['Cause'].cat.categories
+
+
Index(['Fault Code/Error Code', 'Obstacle Detection', 'Other Road Users',
+       'Priority Zone', 'Shuttle Manually Deviated from Approved Path',
+       'Signal Loss', 'Signalized Intersection', 'Station Blocked',
+       'Vegetation'],
+      dtype='object')
+
+
+
+
disengagements_datetime_is_index = disengagements.set_index('Incident Datetime')
+disengagements_datetime_is_index
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LocationWeatherVehicle Speed in Miles per HourInitiated byCauseweek_of_yearweek_of_pilot
Incident Datetime
2023-03-07 15:00:00+00:0035.7849964, -78.8268094Sunny;2OperatorFault Code/Error Code101
2023-03-07 19:00:00+00:0035.7847312, -78.8245051Sunny;5OperatorStation Blocked101
2023-03-07 19:30:00+00:0035.7824658, -78.8244159Sunny;5OperatorStation Blocked101
2023-03-07 20:15:00+00:0035.7824658, -78.8244159Sunny;4OperatorStation Blocked101
2023-03-08 15:00:00+00:0035.7852558, -78.8273737Sunny;2OperatorShuttle Manually Deviated from Approved Path101
........................
2023-06-01 20:00:00+00:0035.783456, -78.821639Sunny;5OperatorSignal Loss2213
2023-06-02 14:32:00+00:0035.7819145, -78.8235603Sunny;4OperatorStation Blocked2213
2023-06-02 14:35:00+00:0035.7813188, -78.8256601Sunny;3OperatorStation Blocked2213
2023-06-02 14:44:00+00:0035.7847325, -78.824496Sunny;4OperatorObstacle Detection2213
2023-06-02 15:01:00+00:0035.7841086, -78.8261962Sunny;3OperatorSignalized Intersection2213
+ +

179 rows × 7 columns

+
+
+
+
+
disengagements_datetime_is_index.index=disengagements_datetime_is_index.index.tz_convert(tz='US/Eastern')
+disengagements_datetime_is_index
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LocationWeatherVehicle Speed in Miles per HourInitiated byCauseweek_of_yearweek_of_pilot
Incident Datetime
2023-03-07 10:00:00-05:0035.7849964, -78.8268094Sunny;2OperatorFault Code/Error Code101
2023-03-07 14:00:00-05:0035.7847312, -78.8245051Sunny;5OperatorStation Blocked101
2023-03-07 14:30:00-05:0035.7824658, -78.8244159Sunny;5OperatorStation Blocked101
2023-03-07 15:15:00-05:0035.7824658, -78.8244159Sunny;4OperatorStation Blocked101
2023-03-08 10:00:00-05:0035.7852558, -78.8273737Sunny;2OperatorShuttle Manually Deviated from Approved Path101
........................
2023-06-01 16:00:00-04:0035.783456, -78.821639Sunny;5OperatorSignal Loss2213
2023-06-02 10:32:00-04:0035.7819145, -78.8235603Sunny;4OperatorStation Blocked2213
2023-06-02 10:35:00-04:0035.7813188, -78.8256601Sunny;3OperatorStation Blocked2213
2023-06-02 10:44:00-04:0035.7847325, -78.824496Sunny;4OperatorObstacle Detection2213
2023-06-02 11:01:00-04:0035.7841086, -78.8261962Sunny;3OperatorSignalized Intersection2213
+ +

179 rows × 7 columns

+
+
+
+
+
disengagements_datetime_is_index.dtypes
+
+
Location                             object
+Weather                              object
+Vehicle Speed in Miles per Hour       int64
+Initiated by                       category
+Cause                              category
+week_of_year                         UInt32
+week_of_pilot                        UInt32
+dtype: object
+
+
+
+
one_hot = disengagements_datetime_is_index.Weather.str.get_dummies(sep=';')
+one_hot
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CloudyPartly CloudyRainSunnyWindy
Incident Datetime
2023-03-07 10:00:00-05:0000010
2023-03-07 14:00:00-05:0000010
2023-03-07 14:30:00-05:0000010
2023-03-07 15:15:00-05:0000010
2023-03-08 10:00:00-05:0000010
..................
2023-06-01 16:00:00-04:0000010
2023-06-02 10:32:00-04:0000010
2023-06-02 10:35:00-04:0000010
2023-06-02 10:44:00-04:0000010
2023-06-02 11:01:00-04:0000010
+ +

179 rows × 5 columns

+
+
+
+
+
one_hot.columns = 'Weather_' + one_hot.columns
+one_hot
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Weather_CloudyWeather_Partly CloudyWeather_RainWeather_SunnyWeather_Windy
Incident Datetime
2023-03-07 10:00:00-05:0000010
2023-03-07 14:00:00-05:0000010
2023-03-07 14:30:00-05:0000010
2023-03-07 15:15:00-05:0000010
2023-03-08 10:00:00-05:0000010
..................
2023-06-01 16:00:00-04:0000010
2023-06-02 10:32:00-04:0000010
2023-06-02 10:35:00-04:0000010
2023-06-02 10:44:00-04:0000010
2023-06-02 11:01:00-04:0000010
+ +

179 rows × 5 columns

+
+
+
+
+
one_hot_cause = disengagements_datetime_is_index.Cause.str.get_dummies()
+one_hot_cause.columns = 'Cause_' + one_hot_cause.columns
+one_hot_cause
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Cause_Fault Code/Error CodeCause_Obstacle DetectionCause_Other Road UsersCause_Priority ZoneCause_Shuttle Manually Deviated from Approved PathCause_Signal LossCause_Signalized IntersectionCause_Station BlockedCause_Vegetation
Incident Datetime
2023-03-07 10:00:00-05:00100000000
2023-03-07 14:00:00-05:00000000010
2023-03-07 14:30:00-05:00000000010
2023-03-07 15:15:00-05:00000000010
2023-03-08 10:00:00-05:00000010000
..............................
2023-06-01 16:00:00-04:00000001000
2023-06-02 10:32:00-04:00000000010
2023-06-02 10:35:00-04:00000000010
2023-06-02 10:44:00-04:00010000000
2023-06-02 11:01:00-04:00000000100
+ +

179 rows × 9 columns

+
+
+
+
+
disengagements_datetime_is_index = disengagements_datetime_is_index.drop(['Weather', 'Initiated by', 'Cause'], axis=1)
+cassi_data_one_hot_encoded = pd.concat([disengagements_datetime_is_index, one_hot, one_hot_cause], axis=1)
+cassi_data_one_hot_encoded
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LocationVehicle Speed in Miles per Hourweek_of_yearweek_of_pilotWeather_CloudyWeather_Partly CloudyWeather_RainWeather_SunnyWeather_WindyCause_Fault Code/Error CodeCause_Obstacle DetectionCause_Other Road UsersCause_Priority ZoneCause_Shuttle Manually Deviated from Approved PathCause_Signal LossCause_Signalized IntersectionCause_Station BlockedCause_Vegetation
Incident Datetime
2023-03-07 10:00:00-05:0035.7849964, -78.8268094210100010100000000
2023-03-07 14:00:00-05:0035.7847312, -78.8245051510100010000000010
2023-03-07 14:30:00-05:0035.7824658, -78.8244159510100010000000010
2023-03-07 15:15:00-05:0035.7824658, -78.8244159410100010000000010
2023-03-08 10:00:00-05:0035.7852558, -78.8273737210100010000010000
.........................................................
2023-06-01 16:00:00-04:0035.783456, -78.8216395221300010000001000
2023-06-02 10:32:00-04:0035.7819145, -78.82356034221300010000000010
2023-06-02 10:35:00-04:0035.7813188, -78.82566013221300010000000010
2023-06-02 10:44:00-04:0035.7847325, -78.8244964221300010010000000
2023-06-02 11:01:00-04:0035.7841086, -78.82619623221300010000000100
+ +

179 rows × 18 columns

+
+
+
+
+
cassi_data_one_hot_encoded.index = cassi_data_one_hot_encoded.index.tz_convert(tz='UTC')
+
+
+
cassi_data_one_hot_encoded
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LocationVehicle Speed in Miles per Hourweek_of_yearweek_of_pilotWeather_CloudyWeather_Partly CloudyWeather_RainWeather_SunnyWeather_WindyCause_Fault Code/Error CodeCause_Obstacle DetectionCause_Other Road UsersCause_Priority ZoneCause_Shuttle Manually Deviated from Approved PathCause_Signal LossCause_Signalized IntersectionCause_Station BlockedCause_Vegetation
Incident Datetime
2023-03-07 15:00:00+00:0035.7849964, -78.8268094210100010100000000
2023-03-07 19:00:00+00:0035.7847312, -78.8245051510100010000000010
2023-03-07 19:30:00+00:0035.7824658, -78.8244159510100010000000010
2023-03-07 20:15:00+00:0035.7824658, -78.8244159410100010000000010
2023-03-08 15:00:00+00:0035.7852558, -78.8273737210100010000010000
.........................................................
2023-06-01 20:00:00+00:0035.783456, -78.8216395221300010000001000
2023-06-02 14:32:00+00:0035.7819145, -78.82356034221300010000000010
2023-06-02 14:35:00+00:0035.7813188, -78.82566013221300010000000010
2023-06-02 14:44:00+00:0035.7847325, -78.8244964221300010010000000
2023-06-02 15:01:00+00:0035.7841086, -78.82619623221300010000000100
+ +

179 rows × 18 columns

+
+
+
+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/pandas_plotting_module.html b/_site/notebooks/pandas_plotting_module.html new file mode 100644 index 0000000..e4e09ad --- /dev/null +++ b/_site/notebooks/pandas_plotting_module.html @@ -0,0 +1,850 @@ + + + + + + + + + +Cary Introduction to Python - The pandas.plotting module + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ +
+
+

The pandas.plotting module

+
+ + + +
+ + + + +
+ + + +
+ + +
+

The pandas.plotting module

+

Pandas provides some extra plotting functions for some new plot types.

+
+

About the Data

+

In this notebook, we will be working with Facebook’s stock price throughout 2018 (obtained using the stock_analysis package).

+
+
+

Setup

+
+
%matplotlib inline
+import matplotlib.pyplot as plt
+import numpy as np
+import pandas as pd
+
+fb = pd.read_csv(
+    '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True
+)
+
+
+
+

Scatter matrix

+

Easily create scatter plots between all columns in the dataset:

+
+
from pandas.plotting import scatter_matrix
+scatter_matrix(fb, figsize=(10, 10))
+
+
array([[<Axes: xlabel='open', ylabel='open'>,
+        <Axes: xlabel='high', ylabel='open'>,
+        <Axes: xlabel='low', ylabel='open'>,
+        <Axes: xlabel='close', ylabel='open'>,
+        <Axes: xlabel='volume', ylabel='open'>],
+       [<Axes: xlabel='open', ylabel='high'>,
+        <Axes: xlabel='high', ylabel='high'>,
+        <Axes: xlabel='low', ylabel='high'>,
+        <Axes: xlabel='close', ylabel='high'>,
+        <Axes: xlabel='volume', ylabel='high'>],
+       [<Axes: xlabel='open', ylabel='low'>,
+        <Axes: xlabel='high', ylabel='low'>,
+        <Axes: xlabel='low', ylabel='low'>,
+        <Axes: xlabel='close', ylabel='low'>,
+        <Axes: xlabel='volume', ylabel='low'>],
+       [<Axes: xlabel='open', ylabel='close'>,
+        <Axes: xlabel='high', ylabel='close'>,
+        <Axes: xlabel='low', ylabel='close'>,
+        <Axes: xlabel='close', ylabel='close'>,
+        <Axes: xlabel='volume', ylabel='close'>],
+       [<Axes: xlabel='open', ylabel='volume'>,
+        <Axes: xlabel='high', ylabel='volume'>,
+        <Axes: xlabel='low', ylabel='volume'>,
+        <Axes: xlabel='close', ylabel='volume'>,
+        <Axes: xlabel='volume', ylabel='volume'>]], dtype=object)
+
+
+
+
+

+
+
+
+
+

Changing the diagonal from histograms to KDE:

+
+
scatter_matrix(fb, figsize=(10, 10), diagonal='kde')
+
+
array([[<Axes: xlabel='open', ylabel='open'>,
+        <Axes: xlabel='high', ylabel='open'>,
+        <Axes: xlabel='low', ylabel='open'>,
+        <Axes: xlabel='close', ylabel='open'>,
+        <Axes: xlabel='volume', ylabel='open'>],
+       [<Axes: xlabel='open', ylabel='high'>,
+        <Axes: xlabel='high', ylabel='high'>,
+        <Axes: xlabel='low', ylabel='high'>,
+        <Axes: xlabel='close', ylabel='high'>,
+        <Axes: xlabel='volume', ylabel='high'>],
+       [<Axes: xlabel='open', ylabel='low'>,
+        <Axes: xlabel='high', ylabel='low'>,
+        <Axes: xlabel='low', ylabel='low'>,
+        <Axes: xlabel='close', ylabel='low'>,
+        <Axes: xlabel='volume', ylabel='low'>],
+       [<Axes: xlabel='open', ylabel='close'>,
+        <Axes: xlabel='high', ylabel='close'>,
+        <Axes: xlabel='low', ylabel='close'>,
+        <Axes: xlabel='close', ylabel='close'>,
+        <Axes: xlabel='volume', ylabel='close'>],
+       [<Axes: xlabel='open', ylabel='volume'>,
+        <Axes: xlabel='high', ylabel='volume'>,
+        <Axes: xlabel='low', ylabel='volume'>,
+        <Axes: xlabel='close', ylabel='volume'>,
+        <Axes: xlabel='volume', ylabel='volume'>]], dtype=object)
+
+
+
+
+

+
+
+
+
+
+
+

Lag plot

+

Lag plots let us see how the variable correlates with past observations of itself. Random data has no pattern:

+
+
from pandas.plotting import lag_plot
+np.random.seed(0) # make this repeatable
+lag_plot(pd.Series(np.random.random(size=200)))
+
+
+
+

+
+
+
+
+

Data with some level of correlation to itself (autocorrelation) may have patterns. Stock prices are highly autocorrelated:

+
+
lag_plot(fb.close)
+
+
+
+

+
+
+
+
+

The default lag is 1, but we can alter this with the lag parameter. Let’s look at a 5 day lag (a week of trading activity):

+
+
lag_plot(fb.close, lag=5)
+
+
+
+

+
+
+
+
+
+
+

Autocorrelation plots

+

We can use the autocorrelation plot to see if this relationship may be meaningful or is just noise. Random data will not have any significant autocorrelation (it stays within the bounds below):

+
+
from pandas.plotting import autocorrelation_plot
+np.random.seed(0) # make this repeatable
+autocorrelation_plot(pd.Series(np.random.random(size=200)))
+
+
+
+

+
+
+
+
+

Stock data, on the other hand, does have significant autocorrelation:

+
+
autocorrelation_plot(fb.close)
+
+
+
+

+
+
+
+
+
+
+

Bootstrap plot

+

This plot helps us understand the uncertainty in our summary statistics:

+
+
from pandas.plotting import bootstrap_plot
+fig = bootstrap_plot(fb.volume, fig=plt.figure(figsize=(10, 6)))
+
+
+
+

+
+
+
+
+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/pandas_plotting_module_files/figure-html/cell-10-output-1.png b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-10-output-1.png new file mode 100644 index 0000000..66eddd5 Binary files /dev/null and b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-10-output-1.png differ diff --git a/_site/notebooks/pandas_plotting_module_files/figure-html/cell-3-output-2.png b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-3-output-2.png new file mode 100644 index 0000000..e769890 Binary files /dev/null and b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-3-output-2.png differ diff --git a/_site/notebooks/pandas_plotting_module_files/figure-html/cell-4-output-2.png b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-4-output-2.png new file mode 100644 index 0000000..09998c3 Binary files /dev/null and b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-4-output-2.png differ diff --git a/_site/notebooks/pandas_plotting_module_files/figure-html/cell-5-output-1.png b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-5-output-1.png new file mode 100644 index 0000000..ef65ea8 Binary files /dev/null and b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-5-output-1.png differ diff --git a/_site/notebooks/pandas_plotting_module_files/figure-html/cell-6-output-1.png b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-6-output-1.png new file mode 100644 index 0000000..5ef94df Binary files /dev/null and b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-6-output-1.png differ diff --git a/_site/notebooks/pandas_plotting_module_files/figure-html/cell-7-output-1.png b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-7-output-1.png new file mode 100644 index 0000000..d9e172b Binary files /dev/null and b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-7-output-1.png differ diff --git a/_site/notebooks/pandas_plotting_module_files/figure-html/cell-8-output-1.png b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-8-output-1.png new file mode 100644 index 0000000..c3cf500 Binary files /dev/null and b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-8-output-1.png differ diff --git a/_site/notebooks/pandas_plotting_module_files/figure-html/cell-9-output-1.png b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-9-output-1.png new file mode 100644 index 0000000..c508a36 Binary files /dev/null and b/_site/notebooks/pandas_plotting_module_files/figure-html/cell-9-output-1.png differ diff --git a/_site/notebooks/plotting_with_pandas.html b/_site/notebooks/plotting_with_pandas.html new file mode 100644 index 0000000..a2ea834 --- /dev/null +++ b/_site/notebooks/plotting_with_pandas.html @@ -0,0 +1,1547 @@ + + + + + + + + + +Cary Introduction to Python - Plotting with Pandas + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ +
+
+

Plotting with Pandas

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Plotting with Pandas

+

The plot() method is available on Series and DataFrame objects. Many of the parameters get passed down to matplotlib. The kind argument let’s us vary the plot type. Here are some commonly used parameters:

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterPurposeData Type
kindDetermines the plot typeString
x/yColumn(s) to plot on the x-axis/y-axisString or list
axDraws the plot on the Axes object providedAxes
subplotsDetermines whether to make subplotsBoolean
layoutSpecifies how to arrange the subplotsTuple of (rows, columns)
figsizeSize to make the Figure objectTuple of (width, height)
titleThe title of the plot or subplotsString for the plot title or a list of strings for subplot titles
legendDetermines whether to show the legendBoolean
labelWhat to call an item in the legendString if a single column is being plotted; otherwise, a list of strings
stylematplotlib style strings for each item being plottedString if a single column is being plotted; otherwise, a list of strings
colorThe color to plot the item inString or red, green, blue tuple if a single column is being plotted; otherwise, a list
colormapThe colormap to useString or matplotlib colormap object
logx/logy/loglogDetermines whether to use a logarithmic scale for the x-axis, y-axis, or bothBoolean
xticks/yticksDetermines where to draw the ticks on the x-axis/y-axisList of values
xlim/ylimThe axis limits for the x-axis/y-axisTuple of the form (min, max)
rotThe angle to write the tick labels atInteger
sharex/shareyDetermines whether to have subplots share the x-axis/y-axisBoolean
fontsizeControls the size of the tick labelsInteger
gridTurns on/off the grid linesBoolean
+
+

About the Data

+

In this notebook, we will be working with 3 datasets: - Facebook’s stock price throughout 2018 (obtained using the stock_analysis package) - Earthquake data from September 18, 2018 - October 13, 2018 (obtained from the US Geological Survey (USGS) using the USGS API) - European Centre for Disease Prevention and Control’s (ECDC) daily number of new reported cases of COVID-19 by country worldwide dataset collected on September 19, 2020 via this link

+
+
+

Setup

+
+
%matplotlib inline
+import matplotlib.pyplot as plt
+import numpy as np
+import pandas as pd
+
+fb = pd.read_csv(
+    '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True
+)
+quakes = pd.read_csv('../data/earthquakes.csv')
+covid = pd.read_csv('../data/covid19_cases.csv').assign(
+    date=lambda x: pd.to_datetime(x.dateRep, format='%d/%m/%Y')
+).set_index('date').replace(
+    'United_States_of_America', 'USA'
+).sort_index()['2020-01-18':'2020-09-18']
+
+
+
+

Evolution over time

+

Line plots help us see how a variable changes over time. They are the default for the kind argument, but we can pass kind='line' to be explicit in our intent:

+
+
fb.plot(
+    kind='line',
+    y='open',
+    figsize=(10, 5),
+    style='-b',
+    legend=False,
+    title='Evolution of Facebook Open Price'
+)
+
+
+
+

+
+
+
+
+

We provided the style argument in the previous example; however, we can use the color and linestyle arguments to get the same result:

+
+
fb.plot(
+    kind='line',
+    y='open',
+    figsize=(10, 5),
+    color='blue',
+    linestyle='solid',
+    legend=False,
+    title='Evolution of Facebook Open Price'
+)
+
+
+
+

+
+
+
+
+

We can also plot many lines at once by simply passing a list of the columns to plot:

+
+
fb.first('1W').plot(
+    y=['open', 'high', 'low', 'close'],
+    style=['o-b', '--r', ':k', '.-g'],
+    title='Facebook OHLC Prices during 1st Week of Trading 2018'
+).autoscale()
+
+
+
+

+
+
+
+
+
+

Creating subplots

+

When plotting with pandas, creating subplots is simply a matter of passing subplots=True to the plot() method, and (optionally) specifying the layout in a tuple of (rows, columns):

+
+
fb.plot(
+    kind='line',
+    subplots=True,
+    layout=(3, 2),
+    figsize=(15, 10),
+    title='Facebook Stock 2018'
+)
+
+
array([[<Axes: xlabel='date'>, <Axes: xlabel='date'>],
+       [<Axes: xlabel='date'>, <Axes: xlabel='date'>],
+       [<Axes: xlabel='date'>, <Axes: xlabel='date'>]], dtype=object)
+
+
+
+
+

+
+
+
+
+

Note that we didn’t provide a specific column to plot and pandas plotted all of them for us.

+

Sometimes we want to make subplots that each have a few variables in them for comparison. This can be achieved using the ax parameter. To illustrate this, let’s take a look at daily new COVID-19 cases in China, Spain, Italy, the USA, Brazil, and India:

+
+
new_cases_rolling_average = covid.pivot_table(
+    index=covid.index, 
+    columns='countriesAndTerritories', 
+    values='cases'
+).rolling(7).mean()
+
+

Since there is a lot of fluctuation in these values, we will plot the 7-day moving average of new cases using the rolling() method (discussed in chapter 4). Rather than create a separate plot for each country (which makes it harder to compare) or plot them all together (which will make it difficult to see the smaller values), we will plot countries that have had a similar number of cases in the same subplot:

+
+
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
+
+new_cases_rolling_average[['China']].plot(ax=axes[0], style='-.c')
+new_cases_rolling_average[['Italy', 'Spain']].plot(
+    ax=axes[1], style=['-', '--'], 
+    title='7-day rolling average of new COVID-19 cases\n(source: ECDC)'
+)
+new_cases_rolling_average[['Brazil', 'India', 'USA']]\
+    .plot(ax=axes[2], style=['--', ':', '-'])
+
+
+
+

+
+
+
+
+

NOTE: we specified the line styles here so that the lines can be distinguished in the text as a black and white image.

+

In the previous figure, we were able to compare countries with similar levels of new COVID-19 cases, but we couldn’t compare all of them in the same plot due to scale. One way around this is to use an area plot, which makes it possible for us to visualize the overall 7-day rolling average of new COVID-19 cases and at the same time how much each country is contributing to the total. In the interest of readability, we will group Italy and Spain together and create another category for countries other than the USA, Brazil, and India. The combined height of the plot areas is the overall value, and the height of given shaded region is the value for the individual country.

+
+
plot_cols = ['Brazil', 'India', 'Italy & Spain', 'USA', 'Other']
+grouped = ['Italy', 'Spain']
+other_cols = [
+    col for col in new_cases_rolling_average.columns 
+    if col not in plot_cols
+]
+
+new_cases_rolling_average.sort_index(axis=1).assign(
+    **{
+        'Italy & Spain': lambda x: x[grouped].sum(axis=1),
+        'Other': lambda x: x[other_cols].drop(columns=grouped).sum(axis=1)
+    }
+)[plot_cols].plot(
+    kind='area', figsize=(15, 5), 
+    title='7-day rolling average of new COVID-19 cases\n(source: ECDC)'
+)
+
+
+
+

+
+
+
+
+

Another way to visualize evolution over time is to look at the cumulative sum over time. Let’s plot the cumulative number of COVID-19 cases in China, Spain, Italy, the USA, Brazil, and India, using ax to create subplots as we did in the previous example.

+
+
fig, axes = plt.subplots(1, 3, figsize=(15, 3))
+
+cumulative_covid_cases = covid.groupby(
+    ['countriesAndTerritories', pd.Grouper(freq='1D')]
+).cases.sum().unstack(0).apply('cumsum')
+
+cumulative_covid_cases[['China']].plot(ax=axes[0], style='-.c')
+cumulative_covid_cases[['Italy', 'Spain']].plot(
+    ax=axes[1], style=['-', '--'], 
+    title='Cumulative COVID-19 Cases\n(source: ECDC)'
+)
+cumulative_covid_cases[['Brazil', 'India', 'USA']]\
+    .plot(ax=axes[2], style=['--', ':', '-'])
+
+
+
+

+
+
+
+
+

NOTE: we specified the line styles here so that the lines can be distinguished in the text as a black and white image.

+
+
+
+

Visualizing relationships between variables

+
+

Scatter plots

+

We make scatter plots to help visualize the relationship between two variables. Creating scatter plots requires we pass in kind='scatter' along with a column for the x-axis and a column for the y-axis:

+
+
fb.assign(
+    max_abs_change=fb.high - fb.low
+).plot(
+    kind='scatter', x='volume', y='max_abs_change',
+    title='Facebook Daily High - Low vs. Volume Traded'
+)
+
+
+
+

+
+
+
+
+

The relationship doesn’t seem to be linear, but we can try a log transform on the x-axis since the scales of the axes are very different. With pandas, we simply pass in logx=True:

+
+
fb.assign(
+    max_abs_change=fb.high - fb.low
+).plot(
+    kind='scatter', x='volume', y='max_abs_change',
+    title='Facebook Daily High - Low vs. log(Volume Traded)', 
+    logx=True
+)
+
+
+
+

+
+
+
+
+

With matplotlib, we could use plt.xscale('log') to do the same thing.

+
+
+

Adding Transparency to Plots with alpha

+

Sometimes our plots have many overlapping values, but this can be impossible to see. This can be addressed by increasing the transparency of what we are plotting using the alpha parameter. It is a float in the range [0, 1] where 0 is completely transparent and 1 is completely opaque. By default this is 1, so let’s put in a lower value and re-plot the scatter plot:

+
+
fb.assign(
+    max_abs_change=fb.high - fb.low
+).plot(
+    kind='scatter', x='volume', y='max_abs_change',
+    title='Facebook Daily High - Low vs. log(Volume Traded)',
+    logx=True, alpha=0.25
+)
+
+
+
+

+
+
+
+
+
+
+

Hexbins

+

In the previous example, we can start to see the overlaps, but it is still difficult. Hexbins are another plot type that divide up the plot into hexagons, which are shaded according to the density of points there. With pandas, this is the hexbin value for the kind argument. It may also be necessary to tweak the gridsize, which determines the number of hexagons along the y-axis:

+
+
fb.assign(
+    log_volume=np.log(fb.volume),
+    max_abs_change=fb.high - fb.low
+).plot(
+    kind='hexbin',
+    x='log_volume',
+    y='max_abs_change',
+    title='Facebook Daily High - Low vs. log(Volume Traded)',
+    colormap='gray_r',
+    gridsize=20, 
+    sharex=False # we have to pass this to see the x-axis
+)
+
+
+
+

+
+
+
+
+
+
+

Visualizing Correlations with Heatmaps

+

Pandas doesn’t offer heatmaps; however, if we are able to get our data into a matrix, we can use matshow() from matplotlib:

+
+
fig, ax = plt.subplots(figsize=(20, 10))
+
+# calculate the correlation matrix
+fb_corr = fb.assign(
+    log_volume=np.log(fb.volume),
+    max_abs_change=fb.high - fb.low
+).corr()
+
+# create the heatmap and colorbar
+im = ax.matshow(fb_corr, cmap='seismic')
+im.set_clim(-1, 1)
+fig.colorbar(im)
+
+# label the ticks with the column names
+labels = [col.lower() for col in fb_corr.columns]
+ax.set_xticks(ax.get_xticks()[1:-1]) # to handle bug in matplotlib
+ax.set_xticklabels(labels, rotation=45)
+ax.set_yticks(ax.get_yticks()[1:-1]) # to handle bug in matplotlib
+ax.set_yticklabels(labels)
+
+# include the value of the correlation coefficient in the boxes
+for (i, j), coef in np.ndenumerate(fb_corr):
+    ax.text(
+        i, j, fr'$\rho$ = {coef:.2f}', # raw (r), format (f) string
+        ha='center', va='center', 
+        color='white', fontsize=14
+    )
+
+
+
+

+
+
+
+
+

Accessing the values in the correlation matrix can be done with loc[]:

+
+
fb_corr.loc['max_abs_change', ['volume', 'log_volume']]
+
+
volume        0.642027
+log_volume    0.731542
+Name: max_abs_change, dtype: float64
+
+
+
+
+
+

Visualizing distributions

+
+

Histograms

+

With the pandas, making histograms is as easy as passing kind='hist' to the plot() method:

+
+
fb.volume.plot(
+    kind='hist', 
+    title='Histogram of Daily Volume Traded in Facebook Stock'
+)
+plt.xlabel('Volume traded') # label the x-axis (discussed in chapter 6)
+
+
Text(0.5, 0, 'Volume traded')
+
+
+
+
+

+
+
+
+
+

We can overlap histograms to compare distributions provided we use the alpha parameter. For example, let’s compare the usage and magnitude of the various measurement techniques (the magType column) in the data:

+
+
fig, axes = plt.subplots(figsize=(8, 5))
+
+for magtype in quakes.magType.unique():
+    data = quakes.query(f'magType == "{magtype}"').mag
+    if not data.empty:
+        data.plot(
+            kind='hist', ax=axes, alpha=0.4, 
+            label=magtype, legend=True,
+            title='Comparing histograms of earthquake magnitude by magType'
+        )
+
+plt.xlabel('magnitude') # label the x-axis (discussed in chapter 6)
+
+
Text(0.5, 0, 'magnitude')
+
+
+
+
+

+
+
+
+
+
+
+

Kernel Density Estimation (KDE)

+

We can pass kind='kde' for an estimate of the probability density function (PDF), which tells us the probability of getting a particular value:

+
+
fb.high.plot(
+    kind='kde', 
+    title='KDE of Daily High Price for Facebook Stock'
+)
+plt.xlabel('Price ($)') # label the x-axis (discussed in chapter 6)
+
+
Text(0.5, 0, 'Price ($)')
+
+
+
+
+

+
+
+
+
+
+
+

Adding to the result of plot()

+

The plot() method returns an Axes object. We can store this for additional customization of the plot, or we can pass this into another call to plot() as the ax argument to add to the original plot.

+

It can often be helpful to view the KDE superimposed on top of the histogram, which can be achieved with this strategy:

+
+
ax = fb.high.plot(kind='hist', density=True, alpha=0.5)
+fb.high.plot(
+    ax=ax, kind='kde', color='blue', 
+    title='Distribution of Facebook Stock\'s Daily High Price in 2018'
+)
+plt.xlabel('Price ($)') # label the x-axis (discussed in chapter 6)
+
+
Text(0.5, 0, 'Price ($)')
+
+
+
+
+

+
+
+
+
+
+
+

Plotting the ECDF

+

In some cases, we are more interested in the probability of getting less than or equal to that value (or greater than or equal), which we can see with the cumulative disribution function (CDF). Using the statsmodels package, we can estimate the CDF giving us the empirical cumulative distribution function (ECDF):

+
+
from statsmodels.distributions.empirical_distribution import ECDF
+
+ecdf = ECDF(quakes.query('magType == "ml"').mag)
+plt.plot(ecdf.x, ecdf.y)
+
+# axis labels (we will cover this in chapter 6)
+plt.xlabel('mag') # add x-axis label 
+plt.ylabel('cumulative probability') # add y-axis label
+
+# add title (we will cover this in chapter 6)
+plt.title('ECDF of earthquake magnitude with magType ml')
+
+
Text(0.5, 1.0, 'ECDF of earthquake magnitude with magType ml')
+
+
+
+
+

+
+
+
+
+

This ECDF tells us the probability of getting an earthquake with magnitude of 3 or less using the ml scale is 98%:

+
+
from statsmodels.distributions.empirical_distribution import ECDF
+
+ecdf = ECDF(quakes.query('magType == "ml"').mag)
+plt.plot(ecdf.x, ecdf.y)
+
+# formatting below will all be covered in chapter 6
+# axis labels
+plt.xlabel('mag') # add x-axis label 
+plt.ylabel('cumulative probability') # add y-axis label
+
+# add reference lines for interpreting the ECDF for mag <= 3 
+plt.plot(
+    [3, 3], [0, .98], '--k', 
+    [-1.5, 3], [0.98, 0.98], '--k', alpha=0.4
+)
+
+# set axis ranges
+plt.ylim(0, None)
+plt.xlim(-1.25, None)
+
+# add a title
+plt.title('P(mag <= 3) = 98%')
+
+
Text(0.5, 1.0, 'P(mag <= 3) = 98%')
+
+
+
+
+

+
+
+
+
+
+
+

Box plots

+

To make box plots with pandas, we pass kind='box' to the plot() method:

+
+
fb.iloc[:,:4].plot(kind='box', title='Facebook OHLC Prices Box Plot')
+plt.ylabel('price ($)') # label the y-axis (discussed in chapter 6)
+
+
Text(0, 0.5, 'price ($)')
+
+
+
+
+

+
+
+
+
+

If we pass in notch=True, we get a notched box plot. The notch represents a 95% confidence interval around the median, which can be helpful when comparing differences. For an introduction to interpreting a notched box plot, see this Google sites page and this Towards Data Science article.

+
+
fb.iloc[:,:4].plot(kind='box', title='Facebook OHLC Prices Box Plot', notch=True)
+plt.ylabel('price ($)') # label the y-axis (discussed in chapter 6)
+
+
Text(0, 0.5, 'price ($)')
+
+
+
+
+

+
+
+
+
+

This can also be combined with a call to groupby():

+
+
fb.assign(
+    volume_bin=pd.cut(fb.volume, 3, labels=['low', 'med', 'high'])
+).groupby('volume_bin').boxplot(
+    column=['open', 'high', 'low', 'close'],
+    layout=(1, 3), figsize=(12, 3)
+)
+plt.suptitle('Facebook OHLC Box Plots by Volume Traded', y=1.1)
+
+
Text(0.5, 1.1, 'Facebook OHLC Box Plots by Volume Traded')
+
+
+
+
+

+
+
+
+
+

We can use this to see the distribution of magnitudes across the different measurement methods for earthquakes:

+
+
quakes[['mag', 'magType']].groupby('magType').boxplot(
+    figsize=(15, 8), subplots=False
+)
+plt.title('Earthquake Magnitude Box Plots by magType')
+plt.ylabel('magnitude') # label the y-axis (discussed in chapter 6)
+
+
Text(0, 0.5, 'magnitude')
+
+
+
+
+

+
+
+
+
+
+
+
+

Counts and frequencies

+
+

Bar charts

+

Passing kind='barh' gives us horizontal bars while kind='bar' gives us vertical ones. Let’s use horizontal bars to look at the top 15 places for earthquakes in our data:

+
+
quakes.parsed_place.value_counts().iloc[14::-1,].plot(
+    kind='barh', figsize=(10, 5),
+    title='Top 15 Places for Earthquakes '
+          '(September 18, 2018 - October 13, 2018)'
+)
+plt.xlabel('earthquakes') # label the x-axis (discussed in chapter 6)
+
+
Text(0.5, 0, 'earthquakes')
+
+
+
+
+

+
+
+
+
+

We also have data on whether earthquakes were accompanied by tsunamis. Let’s see what the top places for tsunamis are:

+
+
quakes.groupby('parsed_place').tsunami.sum().sort_values().iloc[-10:,].plot(
+    kind='barh', figsize=(10, 5), 
+    title='Top 10 Places for Tsunamis '
+          '(September 18, 2018 - October 13, 2018)'
+)
+plt.xlabel('tsunamis') # label the x-axis (discussed in chapter 6)
+
+
Text(0.5, 0, 'tsunamis')
+
+
+
+
+

+
+
+
+
+

Seeing that Indonesia is the top place for tsunamis during the time period we are looking at, we may want to look how many earthquakes and tsunamis Indonesia gets on a daily basis. We could show this as a line plot or with bars; since we don’t want to interpolate, we will use bars here:

+
+
indonesia_quakes = quakes.query('parsed_place == "Indonesia"').assign(
+    time=lambda x: pd.to_datetime(x.time, unit='ms'),
+    earthquake=1
+).set_index('time').resample('1D').sum()
+
+# format the datetimes in the index for the x-axis
+indonesia_quakes.index = indonesia_quakes.index.strftime('%b\n%d')
+
+indonesia_quakes.plot(
+    y=['earthquake', 'tsunami'], kind='bar', figsize=(15, 3), 
+    rot=0, label=['earthquakes', 'tsunamis'], 
+    title='Earthquakes and Tsunamis in Indonesia '
+          '(September 18, 2018 - October 13, 2018)'
+)
+
+# label the axes (discussed in chapter 6)
+plt.xlabel('date')
+plt.ylabel('count')
+
+
C:\Users\gpower\AppData\Local\Temp\ipykernel_13112\3940988219.py:4: FutureWarning: The default value of numeric_only in DataFrameGroupBy.sum is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.
+  ).set_index('time').resample('1D').sum()
+
+
+
Text(0, 0.5, 'count')
+
+
+
+
+

+
+
+
+
+
+
+

Grouped Bars

+
+
quakes.groupby(['parsed_place', 'tsunami']).mag.count()\
+    .unstack().apply(lambda x: x / x.sum(), axis=1)\
+    .rename(columns={0: 'no', 1: 'yes'})\
+    .sort_values('yes', ascending=False)[7::-1]\
+    .plot.barh(
+        title='Frequency of a tsunami accompanying an earthquake'
+    )
+
+# move legend to the right of the plot
+plt.legend(title='tsunami?', bbox_to_anchor=(1, 0.65))
+
+# label the axes (discussed in chapter 6)
+plt.xlabel('percentage of earthquakes')
+plt.ylabel('')
+
+
Text(0, 0.5, '')
+
+
+
+
+

+
+
+
+
+

Using the kind arugment for vertical bars when the labels for each bar are shorter:

+
+
quakes.magType.value_counts().plot(
+    kind='bar', title='Earthquakes Recorded per magType', rot=0
+)
+
+# label the axes (discussed in chapter 6)
+plt.xlabel('magType')
+plt.ylabel('earthquakes')
+
+
Text(0, 0.5, 'earthquakes')
+
+
+
+
+

+
+
+
+
+
+
+

Stacked bars

+
+
pivot = quakes.assign(
+    mag_bin=lambda x: np.floor(x.mag)
+).pivot_table(
+    index='mag_bin', columns='magType', values='mag', aggfunc='count'
+)
+pivot.plot.bar(
+    stacked=True, rot=0, ylabel='earthquakes', 
+    title='Earthquakes by integer magnitude and magType'
+)
+
+
+
+

+
+
+
+
+
+

Normalized stacked bars

+

Plot the percentages to be better able to see the different magTypes.

+
+
normalized_pivot = pivot.fillna(0).apply(lambda x: x / x.sum(), axis=1)
+ax = normalized_pivot.plot.bar(
+    stacked=True, rot=0, figsize=(10, 5),
+    title='Percentage of earthquakes by integer magnitude for each magType'
+)
+ax.legend(bbox_to_anchor=(1, 0.8)) # move legend to the right of the plot
+plt.ylabel('percentage') # label the y-axis (discussed in chapter 6)
+
+
Text(0, 0.5, 'percentage')
+
+
+
+
+

+
+
+
+
+

We can also create horizontal stacked bars and do so using groupby() and unstack():

+
+
quakes.groupby(['parsed_place', 'tsunami']).mag.count()\
+    .unstack().apply(lambda x: x / x.sum(), axis=1)\
+    .rename(columns={0: 'no', 1: 'yes'})\
+    .sort_values('yes', ascending=False)[7::-1]\
+    .plot.barh(
+        title='Frequency of a tsunami accompanying an earthquake', 
+        stacked=True
+    )
+
+# move legend to the right of the plot
+plt.legend(title='tsunami?', bbox_to_anchor=(1, 0.65))
+
+# label the axes (discussed in chapter 6)
+plt.xlabel('percentage of earthquakes')
+plt.ylabel('')
+
+
Text(0, 0.5, '')
+
+
+
+
+

+
+
+
+
+ + +
+
+
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-10-output-1.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-10-output-1.png new file mode 100644 index 0000000..088c12d Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-10-output-1.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-11-output-1.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-11-output-1.png new file mode 100644 index 0000000..5a09157 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-11-output-1.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-12-output-1.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-12-output-1.png new file mode 100644 index 0000000..3be1a30 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-12-output-1.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-13-output-1.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-13-output-1.png new file mode 100644 index 0000000..9211d7a Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-13-output-1.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-14-output-1.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-14-output-1.png new file mode 100644 index 0000000..406a48c Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-14-output-1.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-15-output-1.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-15-output-1.png new file mode 100644 index 0000000..8a1823d Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-15-output-1.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-17-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-17-output-2.png new file mode 100644 index 0000000..74d070c Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-17-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-18-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-18-output-2.png new file mode 100644 index 0000000..5cd4a2f Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-18-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-19-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-19-output-2.png new file mode 100644 index 0000000..86c30ff Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-19-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-20-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-20-output-2.png new file mode 100644 index 0000000..6d915b0 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-20-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-21-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-21-output-2.png new file mode 100644 index 0000000..403420e Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-21-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-22-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-22-output-2.png new file mode 100644 index 0000000..e346349 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-22-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-23-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-23-output-2.png new file mode 100644 index 0000000..be34bac Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-23-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-24-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-24-output-2.png new file mode 100644 index 0000000..fa8414d Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-24-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-25-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-25-output-2.png new file mode 100644 index 0000000..d6aab6a Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-25-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-26-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-26-output-2.png new file mode 100644 index 0000000..9eebb46 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-26-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-27-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-27-output-2.png new file mode 100644 index 0000000..85c6289 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-27-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-28-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-28-output-2.png new file mode 100644 index 0000000..6cd91e5 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-28-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-29-output-3.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-29-output-3.png new file mode 100644 index 0000000..e8b8c9b Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-29-output-3.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-3-output-1.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-3-output-1.png new file mode 100644 index 0000000..48c5c99 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-3-output-1.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-30-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-30-output-2.png new file mode 100644 index 0000000..3c81963 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-30-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-31-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-31-output-2.png new file mode 100644 index 0000000..e171d60 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-31-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-32-output-1.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-32-output-1.png new file mode 100644 index 0000000..0d51802 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-32-output-1.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-33-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-33-output-2.png new file mode 100644 index 0000000..0ed7bee Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-33-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-34-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-34-output-2.png new file mode 100644 index 0000000..efd2a9c Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-34-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-4-output-1.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-4-output-1.png new file mode 100644 index 0000000..48c5c99 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-4-output-1.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-5-output-1.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-5-output-1.png new file mode 100644 index 0000000..56cc2e8 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-5-output-1.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-6-output-2.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-6-output-2.png new file mode 100644 index 0000000..d342a66 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-6-output-2.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-8-output-1.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-8-output-1.png new file mode 100644 index 0000000..41be4e5 Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-8-output-1.png differ diff --git a/_site/notebooks/plotting_with_pandas_files/figure-html/cell-9-output-1.png b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-9-output-1.png new file mode 100644 index 0000000..06e8bad Binary files /dev/null and b/_site/notebooks/plotting_with_pandas_files/figure-html/cell-9-output-1.png differ diff --git a/_site/notebooks/python_101.html b/_site/notebooks/python_101.html new file mode 100644 index 0000000..6291ed8 --- /dev/null +++ b/_site/notebooks/python_101.html @@ -0,0 +1,1392 @@ + + + + + + + + + +Cary Introduction to Python - Python 101 + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ +
+
+

Python 101

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Python 101

+

This is an optional notebook to get you up to speed with Python in case you are new to Python or need a refresher. The material here is a crash course in Python; I highly recommend the official Python tutorial for a deeper dive. Consider reading this page in the Python docs for background on Python and bookmarking the glossary.

+
+

Basic data types

+
+

Numbers

+

Numbers in Python can be represented as integers (e.g. 5) or floats (e.g. 5.0). We can perform operations on them:

+
+
5 + 6
+
+
11
+
+
+
+
2.5 / 3
+
+
0.8333333333333334
+
+
+
+
+

Booleans

+

We can check for equality giving us a Boolean:

+
+
5 == 6
+
+
False
+
+
+
+
5 < 6
+
+
True
+
+
+

These statements can be combined with logical operators: not, and, or

+
+
(5 < 6) and not (5 == 6)
+
+
True
+
+
+
+
False or True
+
+
True
+
+
+
+
True or False
+
+
True
+
+
+
+
+

Strings

+

Using strings, we can handle text in Python. These values must be surrounded in quotes — single ('...') is the standard, but double ("...") works as well:

+
+
'hello'
+
+
'hello'
+
+
+

We can also perform operations on strings. For example, we can see how long it is with len():

+
+
len('hello')
+
+
5
+
+
+

We can select parts of the string by specifying the index. Note that in Python the 1st character is at index 0:

+
+
'hello'[0]
+
+
'h'
+
+
+

We can concatentate strings with +:

+
+
'hello' + ' ' + 'world'
+
+
'hello world'
+
+
+

We can check if characters are in the string with the in operator:

+
+
'h' in 'hello'
+
+
True
+
+
+
+
+
+

Variables

+

Notice that just typing text causes an error. Errors in Python attempt to clue us in to what went wrong with our code. In this case, we have a NameError exception which tells us that 'hello' is not defined. This means that the Python interpreter looked for a variable named hello, but it didn’t find one.

+
+
hello
+
+
NameError: name 'hello' is not defined
+
+
+

Variables give us a way to store data types. We define a variable using the variable_name = value syntax:

+
+
x = 5
+y = 7
+x + y
+
+
12
+
+
+

The variable name cannot contain spaces; we usually use _ instead. The best variable names are descriptive ones:

+
+
book_title = 'Hands-On Data Analysis with Pandas'
+
+

Variables can be any data type. We can check which one it is with type(), which is a function (more on that later):

+
+
type(x)
+
+
int
+
+
+
+
type(book_title)
+
+
str
+
+
+

If we need to see the value of a variable, we can print it using the print() function:

+
+
print(book_title)
+
+
Hands-On Data Analysis with Pandas
+
+
+
+
+

Collections of Items

+
+

Lists

+

We can store a collection of items in a list:

+
+
['hello', ' ', 'world']
+
+
['hello', ' ', 'world']
+
+
+

The list can be stored in a variable. Note that the items in the list can be of different types:

+
+
my_list = ['hello', 3.8, True, 'Python']
+type(my_list)
+
+
list
+
+
+

We can see how many elements are in the list with len():

+
+
len(my_list)
+
+
4
+
+
+

We can also use the in operator to check if a value is in the list:

+
+
'world' in my_list
+
+
False
+
+
+

We can select items in the list just as we did with strings, by providing the index to select:

+
+
my_list[1]
+
+
3.8
+
+
+

Python also allows us to use negative values, so we can easily select the last one:

+
+
my_list[-1]
+
+
'Python'
+
+
+

Another powerful feature of lists (and strings) is slicing. We can grab the middle 2 elements in the list:

+
+
my_list[1:3]
+
+
[3.8, True]
+
+
+

… or every other one:

+
+
my_list[::2]
+
+
['hello', True]
+
+
+

We can even select the list in reverse:

+
+
my_list[::-1]
+
+
['Python', True, 3.8, 'hello']
+
+
+

Note: This syntax is [start:stop:step] where the selection is inclusive of the start index, but exclusive of the stop index. If start isn’t provided, 0 is used. If stop isn’t provided, the number of elements is used (4, in our case); this works because the stop is exclusive. If step isn’t provided, it is 1.

+

We can use the join() method on a string object to concatenate all the items of a list into single string. The string we call the join() method on will be used as the separator, here we separate with a pipe (|):

+
+
'|'.join(['x', 'y', 'z'])
+
+
'x|y|z'
+
+
+
+
+

Tuples

+

Tuples are similar to lists; however, they can’t be modified after creation i.e. they are immutable. Instead of square brackets, we use parenthesis to create tuples:

+
+
my_tuple = ('a', 5)
+type(my_tuple)
+
+
tuple
+
+
+
+
my_tuple[0]
+
+
'a'
+
+
+

Immutable objects can’t be modified:

+
+
my_tuple[0] = 'b'
+
+
TypeError: 'tuple' object does not support item assignment
+
+
+
+
+

Dictionaries

+

We can store mappings of key-value pairs using dictionaries:

+
+
shopping_list = {
+    'veggies': ['spinach', 'kale', 'beets'],
+    'fruits': 'bananas',
+    'meat': 0    
+}
+type(shopping_list)
+
+
dict
+
+
+

To access the values associated with a specific key, we use the square bracket notation again:

+
+
shopping_list['veggies']
+
+
['spinach', 'kale', 'beets']
+
+
+

We can extract all of the keys with keys():

+
+
shopping_list.keys()
+
+
dict_keys(['veggies', 'fruits', 'meat'])
+
+
+

We can extract all of the values with values():

+
+
shopping_list.values()
+
+
dict_values([['spinach', 'kale', 'beets'], 'bananas', 0])
+
+
+

Finally, we can call items() to get back pairs of (key, value) pairs:

+
+
shopping_list.items()
+
+
dict_items([('veggies', ['spinach', 'kale', 'beets']), ('fruits', 'bananas'), ('meat', 0)])
+
+
+
+
+

Sets

+

A set is a collection of unique items; a common use is to remove duplicates from a list. These are written with curly braces also, but notice there is no key-value mapping:

+
+
my_set = {1, 1, 2, 'a'}
+type(my_set)
+
+
set
+
+
+

How many items are in this set?

+
+
len(my_set)
+
+
3
+
+
+

We put in 4 items but the set only has 3 because duplicates are removed:

+
+
my_set
+
+
{1, 2, 'a'}
+
+
+

We can check if a value is in the set:

+
+
2 in my_set
+
+
True
+
+
+
+
+
+

Functions

+

We can define functions to package up our code for reuse. We have already seen some functions: len(), type(), and print(). They are all functions that take arguments. Note that functions don’t need to accept arguments, in which case they are called without passing in anything (e.g. print() versus print(my_string)).

+

Aside: we can also create lists, sets, dictionaries, and tuples with functions: list(), set(), dict(), and tuple()

+
+

Defining functions

+

We use the def keyword to define functions. Let’s create a function called add() with 2 parameters, x and y, which will be the names the code in the function will use to refer to the arguments we pass in when calling it:

+
+
def add(x, y):
+    """This is a docstring. It is used to explain how the code works and is optional (but encouraged)."""
+    # this is a comment; it allows us to annotate the code
+    print('Performing addition')
+    return x + y
+
+

Once we run the code above, our function is ready to use:

+
+
type(add)
+
+
function
+
+
+

Let’s add some numbers:

+
+
add(1, 2)
+
+
Performing addition
+
+
+
3
+
+
+
+
+

Return values

+

We can store the result in a variable for later:

+
+
result = add(1, 2)
+
+
Performing addition
+
+
+

Notice the print statement wasn’t captured in result. This variable will only have what the function returns. This is what the return line in the function definition did:

+
+
result
+
+
3
+
+
+

Note that functions don’t have to return anything. Consider print():

+
+
print_result = print('hello world')
+
+
hello world
+
+
+

If we take a look at what we got back, we see it is a NoneType object:

+
+
type(print_result)
+
+
NoneType
+
+
+

In Python, the value None represents null values. We can check if our variable is None:

+
+
print_result is None
+
+
True
+
+
+

Warning: make sure to use comparison operators (e.g. >, >=, <, <=, ==, !=) to compare to values other than None.

+
+
+

Function arguments

+

Note that function arguments can be anything, even other functions. We will see several examples of this in the text.

+

The function we defined requires arguments. If we don’t provide them all, it will cause an error:

+
+
add(1)
+
+
TypeError: add() missing 1 required positional argument: 'y'
+
+
+

We can use help() to check what arguments the function needs (notice the docstring ends up here):

+
+
help(add)
+
+
Help on function add in module __main__:
+
+add(x, y)
+    This is a docstring. It is used to explain how the code works and is optional (but encouraged).
+
+
+
+

We will also get errors if we pass in data types that add() can’t work with:

+
+
add(set(), set())
+
+
Performing addition
+
+
+
TypeError: unsupported operand type(s) for +: 'set' and 'set'
+
+
+

We will discuss error handling in the text.

+
+
+
+

Control Flow Statements

+

Sometimes we want to vary the path the code takes based on some criteria. For this we have if, elif, and else. We can use if on its own:

+
+
def make_positive(x):
+    """Returns a positive x"""
+    if x < 0:
+        x *= -1
+    return x
+
+

Calling this function with negative input causes the code under the if statement to run:

+
+
make_positive(-1)
+
+
1
+
+
+

Calling this function with positive input skips the code under the if statement, keeping the number positive:

+
+
make_positive(2)
+
+
2
+
+
+

Sometimes we need an else statement as well:

+
+
def add_or_subtract(operation, x, y):
+    if operation == 'add':
+        return x + y
+    else:
+        return x - y
+
+

This triggers the code under the if statement:

+
+
add_or_subtract('add', 1, 2)
+
+
3
+
+
+

Since the Boolean check in the if statement was False, this triggers the code under the else statement:

+
+
add_or_subtract('subtract', 1, 2)
+
+
-1
+
+
+

For more complicated logic, we can also use elif. We can have any number of elif statements. Optionally, we can include else.

+
+
def calculate(operation, x, y):
+    if operation == 'add':
+        return x + y
+    elif operation == 'subtract':
+        return x - y
+    elif operation == 'multiply':
+        return x * y
+    elif operation == 'division':
+        return x / y
+    else:
+        print("This case hasn't been handled")
+
+

The code keeps checking the conditions in the if statements from top to bottom until it finds multiply:

+
+
calculate('multiply', 3, 4)
+
+
12
+
+
+

The code keeps checking the conditions in the if statements from top to bottom until it hits the else statement:

+
+
calculate('power', 3, 4)
+
+
This case hasn't been handled
+
+
+
+
+

Loops

+
+

while loops

+

With while loops, we can keep running code until some stopping condition is met:

+
+
done = False
+value = 2
+while not done:
+    print('Still going...', value)
+    value *= 2
+    if value > 10:
+        done = True
+
+
Still going... 2
+Still going... 4
+Still going... 8
+
+
+

Note this can also be written as, by moving the condition to the while statement:

+
+
value = 2
+while value < 10:
+    print('Still going...', value)
+    value *= 2
+
+
Still going... 2
+Still going... 4
+Still going... 8
+
+
+
+
+

for loops

+

With for loops, we can run our code for each element in a collection:

+
+
for i in range(5):
+    print(i)
+
+
0
+1
+2
+3
+4
+
+
+

We can use for loops with lists, tuples, sets, and dictionaries as well:

+
+
for element in my_list:
+    print(element)
+
+
hello
+3.8
+True
+Python
+
+
+
+
for key, value in shopping_list.items():
+    print('For', key, 'we need to buy', value)
+
+
For veggies we need to buy ['spinach', 'kale', 'beets']
+For fruits we need to buy bananas
+For meat we need to buy 0
+
+
+

With for loops, we don’t have to worry about checking if we have reached the stopping condition. Conversely, while loops can cause infinite loops if we don’t remember to update variables.

+
+
+
+

Imports

+

We have been working with the portion of Python that is available without importing additional functionality. The Python standard library that comes with the install of Python is broken up into several modules, but we often only need a few. We can import whatever we need: a module in the standard library, a 3rd-party library, or code that we wrote. This is done with an import statement:

+
+
import math
+
+print(math.pi)
+
+
3.141592653589793
+
+
+

If we only need a small piece from that module, we can do the following instead:

+
+
from math import pi
+
+print(pi)
+
+
3.141592653589793
+
+
+

Warning: anything you import is added to the namespace, so if you create a new variable/function/etc. with the same name it will overwrite the previous value. For this reason, we have to be careful with variable names e.g. if you name something sum, you won’t be able to add using the sum() built-in function anymore. Using notebooks or an IDE will help you avoid these issues with syntax highlighting.

+
+
+

Installing 3rd-party Packages

+

We can use pip or conda to install packages, depending on how we created our virtual environment. We will walk through the commands to create virtual environments with conda. The environment MUST be activated before installing the packages for this text; otherwise, it’s possible they interfere with other projects on your machine or vice versa.

+

To install a package, we can use conda install <package_name> to download a package from the default conda channel. Optionally, we can provide a specific version to install conda install pandas==0.23.4. Even further, can define which channel that we install a package from for example we can install a package from the conda-forge channel by with conda install -c conda-forge pandas=0.23.4. Without that specification, we will get the most stable version. When we have many packages to install we will typically use a environment.yml or requirements.txt file: conda env update -f environment.yml from within your active environment or conda env update -n ENVNAME -f environment.yml if you are updating an update you are not actively in.

+

Note: running conda env export ENVNAME > environment.yml will send the list of platform-specific packages installed in the activate environment and their respective versions to the environment.yml file.

+
+
+

Classes

+

So far we have used Python as a functional programming language, but we also have the option to use it for object-oriented programming. You can think of a class as a way to group similar functionality together. Let’s create a calculator class which can handle mathematical operations for us. For this, we use the class keyword and define methods for taking actions on the calculator. These methods are functions that take self as the first argument. When calling them, we don’t pass in anything for that argument (example after this):

+
+
class Calculator:
+    """This is the class docstring."""
+    
+    def __init__(self):
+        """This is a method and it is called when we create an object of type `Calculator`."""
+        self.on = False
+        
+    def turn_on(self):
+        """This method turns on the calculator."""
+        self.on = True
+    
+    def add(self, x, y):
+        """Perform addition if calculator is on"""
+        if self.on:
+            return x + y
+        else:
+            print('the calculator is not on')
+
+

In order to use the calculator, we need to instantiate an instance or object of type Calculator. Since the __init__() method has no parameters other than self, we don’t need to provide anything:

+
+
my_calculator = Calculator()
+
+

Let’s try to add some numbers:

+
+
my_calculator.add(1, 2)
+
+
the calculator is not on
+
+
+

Oops!! The calculator is not on. Let’s turn it on:

+
+
my_calculator.turn_on()
+
+

Let’s try again:

+
+
my_calculator.add(1, 2)
+
+
3
+
+
+

We can access attributes on object with dot notation. In this example, the only attribute is on, and it is set in the __init__() method:

+
+
my_calculator.on
+
+
True
+
+
+

Note that we can also update attributes:

+
+
my_calculator.on = False
+my_calculator.add(1, 2)
+
+
the calculator is not on
+
+
+

Finally, we can use help() to get more information on the object:

+
+
help(my_calculator)
+
+
Help on Calculator in module __main__ object:
+
+class Calculator(builtins.object)
+ |  This is the class docstring.
+ |  
+ |  Methods defined here:
+ |  
+ |  __init__(self)
+ |      This is a method and it is called when we create an object of type `Calculator`.
+ |  
+ |  add(self, x, y)
+ |      Perform addition if calculator is on
+ |  
+ |  turn_on(self)
+ |      This method turns on the calculator.
+ |  
+ |  ----------------------------------------------------------------------
+ |  Data descriptors defined here:
+ |  
+ |  __dict__
+ |      dictionary for instance variables (if defined)
+ |  
+ |  __weakref__
+ |      list of weak references to the object (if defined)
+
+
+
+

… and also for a method:

+
+
help(my_calculator.add)
+
+
Help on method add in module __main__:
+
+add(x, y) method of __main__.Calculator instance
+    Perform addition if calculator is on
+
+
+
+
+
+

Next Steps

+

This was a crash course in Python. This isn’t an exhaustive list of all of the features available to you.

+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/python_errors.html b/_site/notebooks/python_errors.html new file mode 100644 index 0000000..5566e9b --- /dev/null +++ b/_site/notebooks/python_errors.html @@ -0,0 +1,846 @@ + + + + + + + + + +Cary Introduction to Python - Python Errors + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ +
+
+

Python Errors

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Python Errors

+

When an error arises, there will be an error message with the type of error and the line the error occured on. This notebook goes over how to handle the common types of errors and exceptions in Python.

+

I recommend looking at the Python tutorial page for more information on errors. Searching for the error message directly on Google can help the debugging process if there is an error not discussed in this page.

+
+

Syntax Error

+

A SyntaxError occurs when the syntax of your code is incorrect.

+
+
if True 
+print("Hello World")
+
+
SyntaxError: expected ':' (975521850.py, line 1)
+
+
+

A colon is expected after the if statement, which arises the syntax error. The error goes away after adding the colon.

+
+
if True:
+    print("Hello World")
+
+
Hello World
+
+
+
+
+

Name Error

+

A NameError occurs when a variable, function, or module used does not exist. When this happens, it is usually because of a spelling error.

+
+
add
+
+
NameError: name 'add' is not defined
+
+
+
+
string(9)
+
+
NameError: name 'string' is not defined
+
+
+
+
+

Type Error

+

A TypeError occurs when you input an incorrect data type for an operation or function.

+
+
"abc" + 9
+
+
TypeError: can only concatenate str (not "int") to str
+
+
+

In python, you cannot add strings to integers. You can add, however, an integer to an integer or a string to a string with a +.

+
+
9 + 9
+
+
18
+
+
+
+
"abc" + "def"
+
+
'abcdef'
+
+
+
+
+

Zero Division Error

+

A ZeroDivisionError occurs when you try to divide by zero. To fix this, recheck your computation.

+
+
2 / (9 * 0)
+
+
ZeroDivisionError: division by zero
+
+
+
+
#code corrected to no longer divide by zero
+(2 / 9) * 0
+
+
0.0
+
+
+
+
+

Value Error

+

A ValueError occurs when an input for a function is the correct data type but is invalid in regards to the domain of the function. This is most common with mathematical operations.

+
+
import math
+
+math.sqrt(-10)
+
+
ValueError: math domain error
+
+
+

In the example above, you must input a positive number into the sqrt() function. The negative number is still an integer, but it is not in the function’s domain.

+
+
math.sqrt(10)
+
+
3.1622776601683795
+
+
+
+
+

Index Error

+

An IndexError occurs when you try to access an item in a list with an index out of bounds.

+
+
list = [1,2,3,4,5]
+list[5]
+
+
IndexError: list index out of range
+
+
+

The range of a list is [0, n-1], where “n” is the length of the list. So, the list [1,2,3,4,5] has index elements in the range 0-4.

+
+
list[4]
+
+
5
+
+
+
+
+

Module Not Found Error

+

A ModuleNotFoundError occurs when you try to import a module that does not exist. It is a type of ImportError. To fix this error, check if you have installed the module in your python environment from the terminal command-line.

+
+
import pillow
+
+
ModuleNotFoundError: No module named 'pillow'
+
+
+
+
+

Catching Exceptions with Try Statements

+

You can use a try statement to catch errors. A try clause includes the code you want to run that might cause an error. If no error occurs, the try clause runs successfully. If an error does occur, the except clause runs after the line in the try clause that caused an error.

+
+
try:
+    "abc" + 9
+    print("Success")
+except:
+    print("Failure to execute")
+
+
Failure to execute
+
+
+

The except clause above can catch any type of error. However, an except clause can also catch a specific type of error. There can be mulptile except clauses in a try statement to catch the different types of errors.

+
+
try:
+    hello
+    "abc" + 9
+    print("Success")
+except TypeError:
+    print("TypeError failure to execute")
+except NameError:
+    print("NameError failure to execute")
+
+
NameError failure to execute
+
+
+
+
try:
+    list = [1,2,3,4,5]
+    list[5]
+    print("Success")
+except TypeError:
+    print("TypeError failure to execute")
+except NameError:
+    print("NameError failure to execute")
+except IndexError:
+    print("IndexError failure to execute")
+
+
IndexError failure to execute
+
+
+
+
+

Next Steps

+

These are not all the errors that might come up in your coding. If another type of error occurs, you can search the error type on Google to learn more about what has caused it. As always, remember to look at the line resulting in the error for hints on what could have gone wrong!

+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/reading_local_files.html b/_site/notebooks/reading_local_files.html new file mode 100644 index 0000000..f02bc60 --- /dev/null +++ b/_site/notebooks/reading_local_files.html @@ -0,0 +1,1158 @@ + + + + + + + + + +Cary Introduction to Python - An Introduction to Jupyter Notebooks + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ +
+
+

An Introduction to Jupyter Notebooks

+
+ + + +
+ + + + +
+ + + +
+ + +
+

An Introduction to Jupyter Notebooks

+

Jupyter Notebooks are a file format (*.ipynb) that you can execute and explain your code in a step-wise format. > Jupyter Notebooks supports not only code execution in Python, but over 40 languages including R, Lua, Rust, and Julia with numerous kernels.

+

We can write in Markdown to write text with some level of control over your formatting. - Here’s a Link to Basic Markdown - Here’s a link to Markdown’s Extended Syntax

+

Topics We Will Cover - Importing different files and filetypes with pandas - Basic Statistical Analysis of tabular data with pandas and numpy - Creating Charts with python packages from the Matplotlib, Plotly, or HoloViz Ecosystem - Evaluate the potential usecases for each visualization package

+

EvidenceOfLearning
This is you, enjoying the learning process.

+

Step 1: Import pandas into your python program.

+
+
import pandas as pd
+
+# This will import the pandas and numpy packages into your Python program.
+
+df_json = pd.read_json('../data/food-waste-pilot/food-waste-pilot.json')
+df_csv = pd.read_csv('../data/food-waste-pilot/food-waste-pilot.csv')
+df_xlsx = pd.read_excel('../data/food-waste-pilot/food-waste-pilot.xlsx')
+
+
+
df_csv.shape
+
+
(152, 3)
+
+
+
+
df_csv.head() # Grabs the top 5 items in your Dataframe by default.
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Collection DateFood Waste CollectedEstimated Earned Compost Created
02022-02-25250.825
12022-03-02298.830
22022-03-21601.260
32022-03-28857.286
42022-03-30610.861
+ +
+
+
+
+
df_csv.tail() # Grabs the bottom 5 items in your Dataframe by default.
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Collection DateFood Waste CollectedEstimated Earned Compost Created
1472022-10-12385.839
1482022-10-28713.671
1492022-10-31953.495
1502022-12-14694.469
1512023-01-06968.697
+ +
+
+
+
+
df_csv.columns
+
+
Index(['Collection Date', 'Food Waste Collected',
+       'Estimated Earned Compost Created'],
+      dtype='object')
+
+
+
+
df_csv.dtypes # Returns the data types of your columns.
+
+
Collection Date                      object
+Food Waste Collected                float64
+Estimated Earned Compost Created      int64
+dtype: object
+
+
+
+
df_csv.describe()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Food Waste CollectedEstimated Earned Compost Created
count152.000000152.000000
mean526.87368452.611842
std197.83807519.787631
min0.0000000.000000
25%398.05000039.750000
50%531.50000053.000000
75%658.90000066.000000
max1065.800000107.000000
+ +
+
+
+
+
df_csv.info() # Returns index, column names, a count of Non-Null values, and data types.
+
+
<class 'pandas.core.frame.DataFrame'>
+RangeIndex: 152 entries, 0 to 151
+Data columns (total 3 columns):
+ #   Column                            Non-Null Count  Dtype  
+---  ------                            --------------  -----  
+ 0   Collection Date                   152 non-null    object 
+ 1   Food Waste Collected              152 non-null    float64
+ 2   Estimated Earned Compost Created  152 non-null    int64  
+dtypes: float64(1), int64(1), object(1)
+memory usage: 3.7+ KB
+
+
+

There are multiple methods to do type conversion using pandas as well.

+
+
# Oh no, we can see that our Collection Date is not the data type that we want, we need to convert it to a date value.
+
+df_csv['Collection Date'] = pd.to_datetime(df_csv['Collection Date'])
+
+
+
df_csv.info()
+
+
<class 'pandas.core.frame.DataFrame'>
+RangeIndex: 152 entries, 0 to 151
+Data columns (total 3 columns):
+ #   Column                            Non-Null Count  Dtype         
+---  ------                            --------------  -----         
+ 0   Collection Date                   152 non-null    datetime64[ns]
+ 1   Food Waste Collected              152 non-null    float64       
+ 2   Estimated Earned Compost Created  152 non-null    int64         
+dtypes: datetime64[ns](1), float64(1), int64(1)
+memory usage: 3.7 KB
+
+
+
+
# An alternative way to do this date conversion:
+
+df_csv['Collection Date'] = df_csv['Collection Date'].apply(pd.to_datetime)
+
+
+
# astype() is more generic method to convert data types
+
+df_csv['Collection Date'] = df_csv['Collection Date'].astype('datetime64[ns]')
+
+
+
df_csv.dtypes
+
+
Collection Date                     datetime64[ns]
+Food Waste Collected                       float64
+Estimated Earned Compost Created             int64
+dtype: object
+
+
+
+
# Now that we have converted our Collection Date column to a datetime data type, we can use the dt.day_name() method to create a new column that contains the day of the week.
+
+df_csv['Day of Week'] = df_csv['Collection Date'].dt.day_name()
+
+
+
# What if we want to know the date that we collected the most food waste?
+
+df_csv.loc[
+    df_csv['Food Waste Collected'].idxmax(),
+    ['Collection Date']
+]
+
+
Collection Date    2022-08-10 00:00:00
+Name: 20, dtype: object
+
+
+
+
# If you wanted to see our top 10 collection dates, you could do this:
+
+df_csv.nlargest(10,'Food Waste Collected')
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Collection DateFood Waste CollectedEstimated Earned Compost CreatedDay of Week
202022-08-101065.8107Wednesday
1242022-12-27987.499Tuesday
482022-09-12977.898Monday
1512023-01-06968.697Friday
1492022-10-31953.495Monday
32022-03-28857.286Monday
1232022-11-28844.484Monday
572022-12-05834.483Monday
912022-12-30815.482Friday
1372022-07-18807.881Monday
+ +
+
+
+
+
df_csv.nsmallest(10,'Food Waste Collected')
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Collection DateFood Waste CollectedEstimated Earned Compost CreatedDay of Week
532022-11-110.00Friday
952023-01-160.00Monday
1142022-09-050.00Monday
972022-02-0959.06Wednesday
362022-02-16102.810Wednesday
632022-02-21183.818Monday
612022-02-11197.020Friday
392022-04-15200.820Friday
622022-02-18202.820Friday
292022-12-10205.821Saturday
+ +
+
+
+
+
df_csv.plot()
+
+
+
+

+
+
+
+
+
+

You have to make sure that pandas parses your dates

+
+
df_csv_parsed_dates = pd.read_csv('../data/food-waste-pilot/food-waste-pilot.csv', parse_dates=True, index_col="Collection Date")
+
+
+
df_csv_parsed_dates.plot()
+
+
+
+

+
+
+
+
+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/reading_local_files_files/figure-html/cell-19-output-1.png b/_site/notebooks/reading_local_files_files/figure-html/cell-19-output-1.png new file mode 100644 index 0000000..0196671 Binary files /dev/null and b/_site/notebooks/reading_local_files_files/figure-html/cell-19-output-1.png differ diff --git a/_site/notebooks/reading_local_files_files/figure-html/cell-21-output-1.png b/_site/notebooks/reading_local_files_files/figure-html/cell-21-output-1.png new file mode 100644 index 0000000..9f287a7 Binary files /dev/null and b/_site/notebooks/reading_local_files_files/figure-html/cell-21-output-1.png differ diff --git a/_site/notebooks/wide_vs_long.html b/_site/notebooks/wide_vs_long.html new file mode 100644 index 0000000..062570b --- /dev/null +++ b/_site/notebooks/wide_vs_long.html @@ -0,0 +1,1046 @@ + + + + + + + + + +Cary Introduction to Python - Wide vs. Long Format Data + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ +
+
+

Wide vs. Long Format Data

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Wide vs. Long Format Data

+
+

About the data

+

In this notebook, we will be using daily temperature data from the National Centers for Environmental Information (NCEI) API. We will use the Global Historical Climatology Network - Daily (GHCND) dataset for the Boonton 1 station (GHCND:USC00280907); see the documentation here.

+

Note: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.

+
+
+

Setup

+
+
import matplotlib.pyplot as plt
+import pandas as pd
+
+wide_df = pd.read_csv('../data/wide_data.csv', parse_dates=['date'])
+long_df = pd.read_csv(
+    '../data/long_data.csv', 
+    usecols=['date', 'datatype', 'value'], 
+    parse_dates=['date']
+)[['date', 'datatype', 'value']] # sort columns
+
+
+
+

Wide format

+

Our variables each have their own column:

+
+
wide_df.head(6)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
dateTMAXTMINTOBS
02018-10-0121.18.913.9
12018-10-0223.913.917.2
22018-10-0325.015.616.1
32018-10-0422.811.711.7
42018-10-0523.311.718.9
52018-10-0620.013.316.1
+ +
+
+
+

Describing all the columns is easy:

+
+
wide_df.describe(include='all')
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
dateTMAXTMINTOBS
count3131.00000031.00000031.000000
mean2018-10-16 00:00:0016.8290327.56129010.022581
min2018-10-01 00:00:007.800000-1.100000-1.100000
25%2018-10-08 12:00:0012.7500002.5000005.550000
50%2018-10-16 00:00:0016.1000006.7000008.300000
75%2018-10-23 12:00:0021.95000013.60000016.100000
max2018-10-31 00:00:0026.70000017.80000021.700000
stdNaN5.7149626.5132526.596550
+ +
+
+
+

It’s easy to graph with pandas:

+
+
wide_df.plot(
+    x='date', y=['TMAX', 'TMIN', 'TOBS'], figsize=(15, 5), 
+    title='Temperature in NYC in October 2018'
+).set_ylabel('Temperature in Celsius')
+plt.show()
+
+
+
+

+
+
+
+
+
+
+

Long format

+

Our variable names are now in the datatype column and their values are in the value column. We now have 3 rows for each date, since we have 3 different datatypes:

+
+
long_df.head(6)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypevalue
02018-10-01TMAX21.1
12018-10-01TMIN8.9
22018-10-01TOBS13.9
32018-10-02TMAX23.9
42018-10-02TMIN13.9
52018-10-02TOBS17.2
+ +
+
+
+

Since we have many rows for the same date, using describe() is not that helpful:

+
+
long_df.describe(include='all')
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypevalue
count939393.000000
uniqueNaN3NaN
topNaNTMAXNaN
freqNaN31NaN
mean2018-10-16 00:00:00NaN11.470968
min2018-10-01 00:00:00NaN-1.100000
25%2018-10-08 00:00:00NaN6.700000
50%2018-10-16 00:00:00NaN11.700000
75%2018-10-24 00:00:00NaN17.200000
max2018-10-31 00:00:00NaN26.700000
stdNaNNaN7.362354
+ +
+
+
+

Plotting long format data in pandas can be rather tricky. Instead we use seaborn:

+
+
import seaborn as sns
+
+sns.set(rc={'figure.figsize': (15, 5)}, style='white')
+
+ax = sns.lineplot(
+    data=long_df, x='date', y='value', hue='datatype'
+)
+ax.set_ylabel('Temperature in Celsius')
+ax.set_title('Temperature in NYC in October 2018')
+plt.show()
+
+
+
+

+
+
+
+
+

With long data and seaborn, we can easily facet our plots:

+
+
sns.set(
+    rc={'figure.figsize': (20, 10)}, style='white', font_scale=2
+)
+
+g = sns.FacetGrid(long_df, col='datatype', height=10)
+g = g.map(plt.plot, 'date', 'value')
+g.set_titles(size=25)
+g.set_xticklabels(rotation=45)
+plt.show()
+
+
+
+

+
+
+
+
+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/_site/notebooks/wide_vs_long_files/figure-html/cell-5-output-1.png b/_site/notebooks/wide_vs_long_files/figure-html/cell-5-output-1.png new file mode 100644 index 0000000..75fc111 Binary files /dev/null and b/_site/notebooks/wide_vs_long_files/figure-html/cell-5-output-1.png differ diff --git a/_site/notebooks/wide_vs_long_files/figure-html/cell-8-output-1.png b/_site/notebooks/wide_vs_long_files/figure-html/cell-8-output-1.png new file mode 100644 index 0000000..79b132a Binary files /dev/null and b/_site/notebooks/wide_vs_long_files/figure-html/cell-8-output-1.png differ diff --git a/_site/notebooks/wide_vs_long_files/figure-html/cell-9-output-1.png b/_site/notebooks/wide_vs_long_files/figure-html/cell-9-output-1.png new file mode 100644 index 0000000..87286f7 Binary files /dev/null and b/_site/notebooks/wide_vs_long_files/figure-html/cell-9-output-1.png differ diff --git a/_site/search.json b/_site/search.json new file mode 100644 index 0000000..0a75e6a --- /dev/null +++ b/_site/search.json @@ -0,0 +1,959 @@ +[ + { + "objectID": "SECURITY.html", + "href": "SECURITY.html", + "title": "Security Policy", + "section": "", + "text": "File the vulnerability as an issue." + }, + { + "objectID": "SECURITY.html#reporting-a-vulnerability", + "href": "SECURITY.html#reporting-a-vulnerability", + "title": "Security Policy", + "section": "", + "text": "File the vulnerability as an issue." + }, + { + "objectID": "notebooks/reading_local_files.html", + "href": "notebooks/reading_local_files.html", + "title": "An Introduction to Jupyter Notebooks", + "section": "", + "text": "Jupyter Notebooks are a file format (*.ipynb) that you can execute and explain your code in a step-wise format. > Jupyter Notebooks supports not only code execution in Python, but over 40 languages including R, Lua, Rust, and Julia with numerous kernels.\nWe can write in Markdown to write text with some level of control over your formatting. - Here’s a Link to Basic Markdown - Here’s a link to Markdown’s Extended Syntax\nTopics We Will Cover - Importing different files and filetypes with pandas - Basic Statistical Analysis of tabular data with pandas and numpy - Creating Charts with python packages from the Matplotlib, Plotly, or HoloViz Ecosystem - Evaluate the potential usecases for each visualization package\n This is you, enjoying the learning process.\nStep 1: Import pandas into your python program.\n\nimport pandas as pd\n\n# This will import the pandas and numpy packages into your Python program.\n\ndf_json = pd.read_json('../data/food-waste-pilot/food-waste-pilot.json')\ndf_csv = pd.read_csv('../data/food-waste-pilot/food-waste-pilot.csv')\ndf_xlsx = pd.read_excel('../data/food-waste-pilot/food-waste-pilot.xlsx')\n\n\ndf_csv.shape\n\n(152, 3)\n\n\n\ndf_csv.head() # Grabs the top 5 items in your Dataframe by default.\n\n\n\n\n\n\n\n\nCollection Date\nFood Waste Collected\nEstimated Earned Compost Created\n\n\n\n\n0\n2022-02-25\n250.8\n25\n\n\n1\n2022-03-02\n298.8\n30\n\n\n2\n2022-03-21\n601.2\n60\n\n\n3\n2022-03-28\n857.2\n86\n\n\n4\n2022-03-30\n610.8\n61\n\n\n\n\n\n\n\n\ndf_csv.tail() # Grabs the bottom 5 items in your Dataframe by default.\n\n\n\n\n\n\n\n\nCollection Date\nFood Waste Collected\nEstimated Earned Compost Created\n\n\n\n\n147\n2022-10-12\n385.8\n39\n\n\n148\n2022-10-28\n713.6\n71\n\n\n149\n2022-10-31\n953.4\n95\n\n\n150\n2022-12-14\n694.4\n69\n\n\n151\n2023-01-06\n968.6\n97\n\n\n\n\n\n\n\n\ndf_csv.columns\n\nIndex(['Collection Date', 'Food Waste Collected',\n 'Estimated Earned Compost Created'],\n dtype='object')\n\n\n\ndf_csv.dtypes # Returns the data types of your columns.\n\nCollection Date object\nFood Waste Collected float64\nEstimated Earned Compost Created int64\ndtype: object\n\n\n\ndf_csv.describe()\n\n\n\n\n\n\n\n\nFood Waste Collected\nEstimated Earned Compost Created\n\n\n\n\ncount\n152.000000\n152.000000\n\n\nmean\n526.873684\n52.611842\n\n\nstd\n197.838075\n19.787631\n\n\nmin\n0.000000\n0.000000\n\n\n25%\n398.050000\n39.750000\n\n\n50%\n531.500000\n53.000000\n\n\n75%\n658.900000\n66.000000\n\n\nmax\n1065.800000\n107.000000\n\n\n\n\n\n\n\n\ndf_csv.info() # Returns index, column names, a count of Non-Null values, and data types.\n\n<class 'pandas.core.frame.DataFrame'>\nRangeIndex: 152 entries, 0 to 151\nData columns (total 3 columns):\n # Column Non-Null Count Dtype \n--- ------ -------------- ----- \n 0 Collection Date 152 non-null object \n 1 Food Waste Collected 152 non-null float64\n 2 Estimated Earned Compost Created 152 non-null int64 \ndtypes: float64(1), int64(1), object(1)\nmemory usage: 3.7+ KB\n\n\nThere are multiple methods to do type conversion using pandas as well.\n\n# Oh no, we can see that our Collection Date is not the data type that we want, we need to convert it to a date value.\n\ndf_csv['Collection Date'] = pd.to_datetime(df_csv['Collection Date'])\n\n\ndf_csv.info()\n\n<class 'pandas.core.frame.DataFrame'>\nRangeIndex: 152 entries, 0 to 151\nData columns (total 3 columns):\n # Column Non-Null Count Dtype \n--- ------ -------------- ----- \n 0 Collection Date 152 non-null datetime64[ns]\n 1 Food Waste Collected 152 non-null float64 \n 2 Estimated Earned Compost Created 152 non-null int64 \ndtypes: datetime64[ns](1), float64(1), int64(1)\nmemory usage: 3.7 KB\n\n\n\n# An alternative way to do this date conversion:\n\ndf_csv['Collection Date'] = df_csv['Collection Date'].apply(pd.to_datetime)\n\n\n# astype() is more generic method to convert data types\n\ndf_csv['Collection Date'] = df_csv['Collection Date'].astype('datetime64[ns]')\n\n\ndf_csv.dtypes\n\nCollection Date datetime64[ns]\nFood Waste Collected float64\nEstimated Earned Compost Created int64\ndtype: object\n\n\n\n# Now that we have converted our Collection Date column to a datetime data type, we can use the dt.day_name() method to create a new column that contains the day of the week.\n\ndf_csv['Day of Week'] = df_csv['Collection Date'].dt.day_name()\n\n\n# What if we want to know the date that we collected the most food waste?\n\ndf_csv.loc[\n df_csv['Food Waste Collected'].idxmax(),\n ['Collection Date']\n]\n\nCollection Date 2022-08-10 00:00:00\nName: 20, dtype: object\n\n\n\n# If you wanted to see our top 10 collection dates, you could do this:\n\ndf_csv.nlargest(10,'Food Waste Collected')\n\n\n\n\n\n\n\n\nCollection Date\nFood Waste Collected\nEstimated Earned Compost Created\nDay of Week\n\n\n\n\n20\n2022-08-10\n1065.8\n107\nWednesday\n\n\n124\n2022-12-27\n987.4\n99\nTuesday\n\n\n48\n2022-09-12\n977.8\n98\nMonday\n\n\n151\n2023-01-06\n968.6\n97\nFriday\n\n\n149\n2022-10-31\n953.4\n95\nMonday\n\n\n3\n2022-03-28\n857.2\n86\nMonday\n\n\n123\n2022-11-28\n844.4\n84\nMonday\n\n\n57\n2022-12-05\n834.4\n83\nMonday\n\n\n91\n2022-12-30\n815.4\n82\nFriday\n\n\n137\n2022-07-18\n807.8\n81\nMonday\n\n\n\n\n\n\n\n\ndf_csv.nsmallest(10,'Food Waste Collected')\n\n\n\n\n\n\n\n\nCollection Date\nFood Waste Collected\nEstimated Earned Compost Created\nDay of Week\n\n\n\n\n53\n2022-11-11\n0.0\n0\nFriday\n\n\n95\n2023-01-16\n0.0\n0\nMonday\n\n\n114\n2022-09-05\n0.0\n0\nMonday\n\n\n97\n2022-02-09\n59.0\n6\nWednesday\n\n\n36\n2022-02-16\n102.8\n10\nWednesday\n\n\n63\n2022-02-21\n183.8\n18\nMonday\n\n\n61\n2022-02-11\n197.0\n20\nFriday\n\n\n39\n2022-04-15\n200.8\n20\nFriday\n\n\n62\n2022-02-18\n202.8\n20\nFriday\n\n\n29\n2022-12-10\n205.8\n21\nSaturday\n\n\n\n\n\n\n\n\ndf_csv.plot()\n\n\n\n\n\n\n\n\n\n\n\ndf_csv_parsed_dates = pd.read_csv('../data/food-waste-pilot/food-waste-pilot.csv', parse_dates=True, index_col=\"Collection Date\")\n\n\ndf_csv_parsed_dates.plot()", + "crumbs": [ + "Home", + "An Introduction to Jupyter Notebooks" + ] + }, + { + "objectID": "notebooks/reading_local_files.html#you-have-to-make-sure-that-pandas-parses-your-dates", + "href": "notebooks/reading_local_files.html#you-have-to-make-sure-that-pandas-parses-your-dates", + "title": "An Introduction to Jupyter Notebooks", + "section": "", + "text": "df_csv_parsed_dates = pd.read_csv('../data/food-waste-pilot/food-waste-pilot.csv', parse_dates=True, index_col=\"Collection Date\")\n\n\ndf_csv_parsed_dates.plot()", + "crumbs": [ + "Home", + "An Introduction to Jupyter Notebooks" + ] + }, + { + "objectID": "notebooks/python_101.html", + "href": "notebooks/python_101.html", + "title": "Python 101", + "section": "", + "text": "This is an optional notebook to get you up to speed with Python in case you are new to Python or need a refresher. The material here is a crash course in Python; I highly recommend the official Python tutorial for a deeper dive. Consider reading this page in the Python docs for background on Python and bookmarking the glossary.\n\n\n\n\nNumbers in Python can be represented as integers (e.g. 5) or floats (e.g. 5.0). We can perform operations on them:\n\n5 + 6\n\n11\n\n\n\n2.5 / 3\n\n0.8333333333333334\n\n\n\n\n\nWe can check for equality giving us a Boolean:\n\n5 == 6\n\nFalse\n\n\n\n5 < 6\n\nTrue\n\n\nThese statements can be combined with logical operators: not, and, or\n\n(5 < 6) and not (5 == 6)\n\nTrue\n\n\n\nFalse or True\n\nTrue\n\n\n\nTrue or False\n\nTrue\n\n\n\n\n\nUsing strings, we can handle text in Python. These values must be surrounded in quotes — single ('...') is the standard, but double (\"...\") works as well:\n\n'hello'\n\n'hello'\n\n\nWe can also perform operations on strings. For example, we can see how long it is with len():\n\nlen('hello')\n\n5\n\n\nWe can select parts of the string by specifying the index. Note that in Python the 1st character is at index 0:\n\n'hello'[0]\n\n'h'\n\n\nWe can concatentate strings with +:\n\n'hello' + ' ' + 'world'\n\n'hello world'\n\n\nWe can check if characters are in the string with the in operator:\n\n'h' in 'hello'\n\nTrue\n\n\n\n\n\n\nNotice that just typing text causes an error. Errors in Python attempt to clue us in to what went wrong with our code. In this case, we have a NameError exception which tells us that 'hello' is not defined. This means that the Python interpreter looked for a variable named hello, but it didn’t find one.\n\nhello\n\nNameError: name 'hello' is not defined\n\n\nVariables give us a way to store data types. We define a variable using the variable_name = value syntax:\n\nx = 5\ny = 7\nx + y\n\n12\n\n\nThe variable name cannot contain spaces; we usually use _ instead. The best variable names are descriptive ones:\n\nbook_title = 'Hands-On Data Analysis with Pandas'\n\nVariables can be any data type. We can check which one it is with type(), which is a function (more on that later):\n\ntype(x)\n\nint\n\n\n\ntype(book_title)\n\nstr\n\n\nIf we need to see the value of a variable, we can print it using the print() function:\n\nprint(book_title)\n\nHands-On Data Analysis with Pandas\n\n\n\n\n\n\n\nWe can store a collection of items in a list:\n\n['hello', ' ', 'world']\n\n['hello', ' ', 'world']\n\n\nThe list can be stored in a variable. Note that the items in the list can be of different types:\n\nmy_list = ['hello', 3.8, True, 'Python']\ntype(my_list)\n\nlist\n\n\nWe can see how many elements are in the list with len():\n\nlen(my_list)\n\n4\n\n\nWe can also use the in operator to check if a value is in the list:\n\n'world' in my_list\n\nFalse\n\n\nWe can select items in the list just as we did with strings, by providing the index to select:\n\nmy_list[1]\n\n3.8\n\n\nPython also allows us to use negative values, so we can easily select the last one:\n\nmy_list[-1]\n\n'Python'\n\n\nAnother powerful feature of lists (and strings) is slicing. We can grab the middle 2 elements in the list:\n\nmy_list[1:3]\n\n[3.8, True]\n\n\n… or every other one:\n\nmy_list[::2]\n\n['hello', True]\n\n\nWe can even select the list in reverse:\n\nmy_list[::-1]\n\n['Python', True, 3.8, 'hello']\n\n\nNote: This syntax is [start:stop:step] where the selection is inclusive of the start index, but exclusive of the stop index. If start isn’t provided, 0 is used. If stop isn’t provided, the number of elements is used (4, in our case); this works because the stop is exclusive. If step isn’t provided, it is 1.\nWe can use the join() method on a string object to concatenate all the items of a list into single string. The string we call the join() method on will be used as the separator, here we separate with a pipe (|):\n\n'|'.join(['x', 'y', 'z'])\n\n'x|y|z'\n\n\n\n\n\nTuples are similar to lists; however, they can’t be modified after creation i.e. they are immutable. Instead of square brackets, we use parenthesis to create tuples:\n\nmy_tuple = ('a', 5)\ntype(my_tuple)\n\ntuple\n\n\n\nmy_tuple[0]\n\n'a'\n\n\nImmutable objects can’t be modified:\n\nmy_tuple[0] = 'b'\n\nTypeError: 'tuple' object does not support item assignment\n\n\n\n\n\nWe can store mappings of key-value pairs using dictionaries:\n\nshopping_list = {\n 'veggies': ['spinach', 'kale', 'beets'],\n 'fruits': 'bananas',\n 'meat': 0 \n}\ntype(shopping_list)\n\ndict\n\n\nTo access the values associated with a specific key, we use the square bracket notation again:\n\nshopping_list['veggies']\n\n['spinach', 'kale', 'beets']\n\n\nWe can extract all of the keys with keys():\n\nshopping_list.keys()\n\ndict_keys(['veggies', 'fruits', 'meat'])\n\n\nWe can extract all of the values with values():\n\nshopping_list.values()\n\ndict_values([['spinach', 'kale', 'beets'], 'bananas', 0])\n\n\nFinally, we can call items() to get back pairs of (key, value) pairs:\n\nshopping_list.items()\n\ndict_items([('veggies', ['spinach', 'kale', 'beets']), ('fruits', 'bananas'), ('meat', 0)])\n\n\n\n\n\nA set is a collection of unique items; a common use is to remove duplicates from a list. These are written with curly braces also, but notice there is no key-value mapping:\n\nmy_set = {1, 1, 2, 'a'}\ntype(my_set)\n\nset\n\n\nHow many items are in this set?\n\nlen(my_set)\n\n3\n\n\nWe put in 4 items but the set only has 3 because duplicates are removed:\n\nmy_set\n\n{1, 2, 'a'}\n\n\nWe can check if a value is in the set:\n\n2 in my_set\n\nTrue\n\n\n\n\n\n\nWe can define functions to package up our code for reuse. We have already seen some functions: len(), type(), and print(). They are all functions that take arguments. Note that functions don’t need to accept arguments, in which case they are called without passing in anything (e.g. print() versus print(my_string)).\nAside: we can also create lists, sets, dictionaries, and tuples with functions: list(), set(), dict(), and tuple()\n\n\nWe use the def keyword to define functions. Let’s create a function called add() with 2 parameters, x and y, which will be the names the code in the function will use to refer to the arguments we pass in when calling it:\n\ndef add(x, y):\n \"\"\"This is a docstring. It is used to explain how the code works and is optional (but encouraged).\"\"\"\n # this is a comment; it allows us to annotate the code\n print('Performing addition')\n return x + y\n\nOnce we run the code above, our function is ready to use:\n\ntype(add)\n\nfunction\n\n\nLet’s add some numbers:\n\nadd(1, 2)\n\nPerforming addition\n\n\n3\n\n\n\n\n\nWe can store the result in a variable for later:\n\nresult = add(1, 2)\n\nPerforming addition\n\n\nNotice the print statement wasn’t captured in result. This variable will only have what the function returns. This is what the return line in the function definition did:\n\nresult\n\n3\n\n\nNote that functions don’t have to return anything. Consider print():\n\nprint_result = print('hello world')\n\nhello world\n\n\nIf we take a look at what we got back, we see it is a NoneType object:\n\ntype(print_result)\n\nNoneType\n\n\nIn Python, the value None represents null values. We can check if our variable is None:\n\nprint_result is None\n\nTrue\n\n\nWarning: make sure to use comparison operators (e.g. >, >=, <, <=, ==, !=) to compare to values other than None.\n\n\n\nNote that function arguments can be anything, even other functions. We will see several examples of this in the text.\nThe function we defined requires arguments. If we don’t provide them all, it will cause an error:\n\nadd(1)\n\nTypeError: add() missing 1 required positional argument: 'y'\n\n\nWe can use help() to check what arguments the function needs (notice the docstring ends up here):\n\nhelp(add)\n\nHelp on function add in module __main__:\n\nadd(x, y)\n This is a docstring. It is used to explain how the code works and is optional (but encouraged).\n\n\n\nWe will also get errors if we pass in data types that add() can’t work with:\n\nadd(set(), set())\n\nPerforming addition\n\n\nTypeError: unsupported operand type(s) for +: 'set' and 'set'\n\n\nWe will discuss error handling in the text.\n\n\n\n\nSometimes we want to vary the path the code takes based on some criteria. For this we have if, elif, and else. We can use if on its own:\n\ndef make_positive(x):\n \"\"\"Returns a positive x\"\"\"\n if x < 0:\n x *= -1\n return x\n\nCalling this function with negative input causes the code under the if statement to run:\n\nmake_positive(-1)\n\n1\n\n\nCalling this function with positive input skips the code under the if statement, keeping the number positive:\n\nmake_positive(2)\n\n2\n\n\nSometimes we need an else statement as well:\n\ndef add_or_subtract(operation, x, y):\n if operation == 'add':\n return x + y\n else:\n return x - y\n\nThis triggers the code under the if statement:\n\nadd_or_subtract('add', 1, 2)\n\n3\n\n\nSince the Boolean check in the if statement was False, this triggers the code under the else statement:\n\nadd_or_subtract('subtract', 1, 2)\n\n-1\n\n\nFor more complicated logic, we can also use elif. We can have any number of elif statements. Optionally, we can include else.\n\ndef calculate(operation, x, y):\n if operation == 'add':\n return x + y\n elif operation == 'subtract':\n return x - y\n elif operation == 'multiply':\n return x * y\n elif operation == 'division':\n return x / y\n else:\n print(\"This case hasn't been handled\")\n\nThe code keeps checking the conditions in the if statements from top to bottom until it finds multiply:\n\ncalculate('multiply', 3, 4)\n\n12\n\n\nThe code keeps checking the conditions in the if statements from top to bottom until it hits the else statement:\n\ncalculate('power', 3, 4)\n\nThis case hasn't been handled\n\n\n\n\n\n\n\nWith while loops, we can keep running code until some stopping condition is met:\n\ndone = False\nvalue = 2\nwhile not done:\n print('Still going...', value)\n value *= 2\n if value > 10:\n done = True\n\nStill going... 2\nStill going... 4\nStill going... 8\n\n\nNote this can also be written as, by moving the condition to the while statement:\n\nvalue = 2\nwhile value < 10:\n print('Still going...', value)\n value *= 2\n\nStill going... 2\nStill going... 4\nStill going... 8\n\n\n\n\n\nWith for loops, we can run our code for each element in a collection:\n\nfor i in range(5):\n print(i)\n\n0\n1\n2\n3\n4\n\n\nWe can use for loops with lists, tuples, sets, and dictionaries as well:\n\nfor element in my_list:\n print(element)\n\nhello\n3.8\nTrue\nPython\n\n\n\nfor key, value in shopping_list.items():\n print('For', key, 'we need to buy', value)\n\nFor veggies we need to buy ['spinach', 'kale', 'beets']\nFor fruits we need to buy bananas\nFor meat we need to buy 0\n\n\nWith for loops, we don’t have to worry about checking if we have reached the stopping condition. Conversely, while loops can cause infinite loops if we don’t remember to update variables.\n\n\n\n\nWe have been working with the portion of Python that is available without importing additional functionality. The Python standard library that comes with the install of Python is broken up into several modules, but we often only need a few. We can import whatever we need: a module in the standard library, a 3rd-party library, or code that we wrote. This is done with an import statement:\n\nimport math\n\nprint(math.pi)\n\n3.141592653589793\n\n\nIf we only need a small piece from that module, we can do the following instead:\n\nfrom math import pi\n\nprint(pi)\n\n3.141592653589793\n\n\nWarning: anything you import is added to the namespace, so if you create a new variable/function/etc. with the same name it will overwrite the previous value. For this reason, we have to be careful with variable names e.g. if you name something sum, you won’t be able to add using the sum() built-in function anymore. Using notebooks or an IDE will help you avoid these issues with syntax highlighting.\n\n\n\nWe can use pip or conda to install packages, depending on how we created our virtual environment. We will walk through the commands to create virtual environments with conda. The environment MUST be activated before installing the packages for this text; otherwise, it’s possible they interfere with other projects on your machine or vice versa.\nTo install a package, we can use conda install <package_name> to download a package from the default conda channel. Optionally, we can provide a specific version to install conda install pandas==0.23.4. Even further, can define which channel that we install a package from for example we can install a package from the conda-forge channel by with conda install -c conda-forge pandas=0.23.4. Without that specification, we will get the most stable version. When we have many packages to install we will typically use a environment.yml or requirements.txt file: conda env update -f environment.yml from within your active environment or conda env update -n ENVNAME -f environment.yml if you are updating an update you are not actively in.\nNote: running conda env export ENVNAME > environment.yml will send the list of platform-specific packages installed in the activate environment and their respective versions to the environment.yml file.\n\n\n\nSo far we have used Python as a functional programming language, but we also have the option to use it for object-oriented programming. You can think of a class as a way to group similar functionality together. Let’s create a calculator class which can handle mathematical operations for us. For this, we use the class keyword and define methods for taking actions on the calculator. These methods are functions that take self as the first argument. When calling them, we don’t pass in anything for that argument (example after this):\n\nclass Calculator:\n \"\"\"This is the class docstring.\"\"\"\n \n def __init__(self):\n \"\"\"This is a method and it is called when we create an object of type `Calculator`.\"\"\"\n self.on = False\n \n def turn_on(self):\n \"\"\"This method turns on the calculator.\"\"\"\n self.on = True\n \n def add(self, x, y):\n \"\"\"Perform addition if calculator is on\"\"\"\n if self.on:\n return x + y\n else:\n print('the calculator is not on')\n\nIn order to use the calculator, we need to instantiate an instance or object of type Calculator. Since the __init__() method has no parameters other than self, we don’t need to provide anything:\n\nmy_calculator = Calculator()\n\nLet’s try to add some numbers:\n\nmy_calculator.add(1, 2)\n\nthe calculator is not on\n\n\nOops!! The calculator is not on. Let’s turn it on:\n\nmy_calculator.turn_on()\n\nLet’s try again:\n\nmy_calculator.add(1, 2)\n\n3\n\n\nWe can access attributes on object with dot notation. In this example, the only attribute is on, and it is set in the __init__() method:\n\nmy_calculator.on\n\nTrue\n\n\nNote that we can also update attributes:\n\nmy_calculator.on = False\nmy_calculator.add(1, 2)\n\nthe calculator is not on\n\n\nFinally, we can use help() to get more information on the object:\n\nhelp(my_calculator)\n\nHelp on Calculator in module __main__ object:\n\nclass Calculator(builtins.object)\n | This is the class docstring.\n | \n | Methods defined here:\n | \n | __init__(self)\n | This is a method and it is called when we create an object of type `Calculator`.\n | \n | add(self, x, y)\n | Perform addition if calculator is on\n | \n | turn_on(self)\n | This method turns on the calculator.\n | \n | ----------------------------------------------------------------------\n | Data descriptors defined here:\n | \n | __dict__\n | dictionary for instance variables (if defined)\n | \n | __weakref__\n | list of weak references to the object (if defined)\n\n\n\n… and also for a method:\n\nhelp(my_calculator.add)\n\nHelp on method add in module __main__:\n\nadd(x, y) method of __main__.Calculator instance\n Perform addition if calculator is on\n\n\n\n\n\n\nThis was a crash course in Python. This isn’t an exhaustive list of all of the features available to you.", + "crumbs": [ + "Home", + "Python 101" + ] + }, + { + "objectID": "notebooks/python_101.html#basic-data-types", + "href": "notebooks/python_101.html#basic-data-types", + "title": "Python 101", + "section": "", + "text": "Numbers in Python can be represented as integers (e.g. 5) or floats (e.g. 5.0). We can perform operations on them:\n\n5 + 6\n\n11\n\n\n\n2.5 / 3\n\n0.8333333333333334\n\n\n\n\n\nWe can check for equality giving us a Boolean:\n\n5 == 6\n\nFalse\n\n\n\n5 < 6\n\nTrue\n\n\nThese statements can be combined with logical operators: not, and, or\n\n(5 < 6) and not (5 == 6)\n\nTrue\n\n\n\nFalse or True\n\nTrue\n\n\n\nTrue or False\n\nTrue\n\n\n\n\n\nUsing strings, we can handle text in Python. These values must be surrounded in quotes — single ('...') is the standard, but double (\"...\") works as well:\n\n'hello'\n\n'hello'\n\n\nWe can also perform operations on strings. For example, we can see how long it is with len():\n\nlen('hello')\n\n5\n\n\nWe can select parts of the string by specifying the index. Note that in Python the 1st character is at index 0:\n\n'hello'[0]\n\n'h'\n\n\nWe can concatentate strings with +:\n\n'hello' + ' ' + 'world'\n\n'hello world'\n\n\nWe can check if characters are in the string with the in operator:\n\n'h' in 'hello'\n\nTrue", + "crumbs": [ + "Home", + "Python 101" + ] + }, + { + "objectID": "notebooks/python_101.html#variables", + "href": "notebooks/python_101.html#variables", + "title": "Python 101", + "section": "", + "text": "Notice that just typing text causes an error. Errors in Python attempt to clue us in to what went wrong with our code. In this case, we have a NameError exception which tells us that 'hello' is not defined. This means that the Python interpreter looked for a variable named hello, but it didn’t find one.\n\nhello\n\nNameError: name 'hello' is not defined\n\n\nVariables give us a way to store data types. We define a variable using the variable_name = value syntax:\n\nx = 5\ny = 7\nx + y\n\n12\n\n\nThe variable name cannot contain spaces; we usually use _ instead. The best variable names are descriptive ones:\n\nbook_title = 'Hands-On Data Analysis with Pandas'\n\nVariables can be any data type. We can check which one it is with type(), which is a function (more on that later):\n\ntype(x)\n\nint\n\n\n\ntype(book_title)\n\nstr\n\n\nIf we need to see the value of a variable, we can print it using the print() function:\n\nprint(book_title)\n\nHands-On Data Analysis with Pandas", + "crumbs": [ + "Home", + "Python 101" + ] + }, + { + "objectID": "notebooks/python_101.html#collections-of-items", + "href": "notebooks/python_101.html#collections-of-items", + "title": "Python 101", + "section": "", + "text": "We can store a collection of items in a list:\n\n['hello', ' ', 'world']\n\n['hello', ' ', 'world']\n\n\nThe list can be stored in a variable. Note that the items in the list can be of different types:\n\nmy_list = ['hello', 3.8, True, 'Python']\ntype(my_list)\n\nlist\n\n\nWe can see how many elements are in the list with len():\n\nlen(my_list)\n\n4\n\n\nWe can also use the in operator to check if a value is in the list:\n\n'world' in my_list\n\nFalse\n\n\nWe can select items in the list just as we did with strings, by providing the index to select:\n\nmy_list[1]\n\n3.8\n\n\nPython also allows us to use negative values, so we can easily select the last one:\n\nmy_list[-1]\n\n'Python'\n\n\nAnother powerful feature of lists (and strings) is slicing. We can grab the middle 2 elements in the list:\n\nmy_list[1:3]\n\n[3.8, True]\n\n\n… or every other one:\n\nmy_list[::2]\n\n['hello', True]\n\n\nWe can even select the list in reverse:\n\nmy_list[::-1]\n\n['Python', True, 3.8, 'hello']\n\n\nNote: This syntax is [start:stop:step] where the selection is inclusive of the start index, but exclusive of the stop index. If start isn’t provided, 0 is used. If stop isn’t provided, the number of elements is used (4, in our case); this works because the stop is exclusive. If step isn’t provided, it is 1.\nWe can use the join() method on a string object to concatenate all the items of a list into single string. The string we call the join() method on will be used as the separator, here we separate with a pipe (|):\n\n'|'.join(['x', 'y', 'z'])\n\n'x|y|z'\n\n\n\n\n\nTuples are similar to lists; however, they can’t be modified after creation i.e. they are immutable. Instead of square brackets, we use parenthesis to create tuples:\n\nmy_tuple = ('a', 5)\ntype(my_tuple)\n\ntuple\n\n\n\nmy_tuple[0]\n\n'a'\n\n\nImmutable objects can’t be modified:\n\nmy_tuple[0] = 'b'\n\nTypeError: 'tuple' object does not support item assignment\n\n\n\n\n\nWe can store mappings of key-value pairs using dictionaries:\n\nshopping_list = {\n 'veggies': ['spinach', 'kale', 'beets'],\n 'fruits': 'bananas',\n 'meat': 0 \n}\ntype(shopping_list)\n\ndict\n\n\nTo access the values associated with a specific key, we use the square bracket notation again:\n\nshopping_list['veggies']\n\n['spinach', 'kale', 'beets']\n\n\nWe can extract all of the keys with keys():\n\nshopping_list.keys()\n\ndict_keys(['veggies', 'fruits', 'meat'])\n\n\nWe can extract all of the values with values():\n\nshopping_list.values()\n\ndict_values([['spinach', 'kale', 'beets'], 'bananas', 0])\n\n\nFinally, we can call items() to get back pairs of (key, value) pairs:\n\nshopping_list.items()\n\ndict_items([('veggies', ['spinach', 'kale', 'beets']), ('fruits', 'bananas'), ('meat', 0)])\n\n\n\n\n\nA set is a collection of unique items; a common use is to remove duplicates from a list. These are written with curly braces also, but notice there is no key-value mapping:\n\nmy_set = {1, 1, 2, 'a'}\ntype(my_set)\n\nset\n\n\nHow many items are in this set?\n\nlen(my_set)\n\n3\n\n\nWe put in 4 items but the set only has 3 because duplicates are removed:\n\nmy_set\n\n{1, 2, 'a'}\n\n\nWe can check if a value is in the set:\n\n2 in my_set\n\nTrue", + "crumbs": [ + "Home", + "Python 101" + ] + }, + { + "objectID": "notebooks/python_101.html#functions", + "href": "notebooks/python_101.html#functions", + "title": "Python 101", + "section": "", + "text": "We can define functions to package up our code for reuse. We have already seen some functions: len(), type(), and print(). They are all functions that take arguments. Note that functions don’t need to accept arguments, in which case they are called without passing in anything (e.g. print() versus print(my_string)).\nAside: we can also create lists, sets, dictionaries, and tuples with functions: list(), set(), dict(), and tuple()\n\n\nWe use the def keyword to define functions. Let’s create a function called add() with 2 parameters, x and y, which will be the names the code in the function will use to refer to the arguments we pass in when calling it:\n\ndef add(x, y):\n \"\"\"This is a docstring. It is used to explain how the code works and is optional (but encouraged).\"\"\"\n # this is a comment; it allows us to annotate the code\n print('Performing addition')\n return x + y\n\nOnce we run the code above, our function is ready to use:\n\ntype(add)\n\nfunction\n\n\nLet’s add some numbers:\n\nadd(1, 2)\n\nPerforming addition\n\n\n3\n\n\n\n\n\nWe can store the result in a variable for later:\n\nresult = add(1, 2)\n\nPerforming addition\n\n\nNotice the print statement wasn’t captured in result. This variable will only have what the function returns. This is what the return line in the function definition did:\n\nresult\n\n3\n\n\nNote that functions don’t have to return anything. Consider print():\n\nprint_result = print('hello world')\n\nhello world\n\n\nIf we take a look at what we got back, we see it is a NoneType object:\n\ntype(print_result)\n\nNoneType\n\n\nIn Python, the value None represents null values. We can check if our variable is None:\n\nprint_result is None\n\nTrue\n\n\nWarning: make sure to use comparison operators (e.g. >, >=, <, <=, ==, !=) to compare to values other than None.\n\n\n\nNote that function arguments can be anything, even other functions. We will see several examples of this in the text.\nThe function we defined requires arguments. If we don’t provide them all, it will cause an error:\n\nadd(1)\n\nTypeError: add() missing 1 required positional argument: 'y'\n\n\nWe can use help() to check what arguments the function needs (notice the docstring ends up here):\n\nhelp(add)\n\nHelp on function add in module __main__:\n\nadd(x, y)\n This is a docstring. It is used to explain how the code works and is optional (but encouraged).\n\n\n\nWe will also get errors if we pass in data types that add() can’t work with:\n\nadd(set(), set())\n\nPerforming addition\n\n\nTypeError: unsupported operand type(s) for +: 'set' and 'set'\n\n\nWe will discuss error handling in the text.", + "crumbs": [ + "Home", + "Python 101" + ] + }, + { + "objectID": "notebooks/python_101.html#control-flow-statements", + "href": "notebooks/python_101.html#control-flow-statements", + "title": "Python 101", + "section": "", + "text": "Sometimes we want to vary the path the code takes based on some criteria. For this we have if, elif, and else. We can use if on its own:\n\ndef make_positive(x):\n \"\"\"Returns a positive x\"\"\"\n if x < 0:\n x *= -1\n return x\n\nCalling this function with negative input causes the code under the if statement to run:\n\nmake_positive(-1)\n\n1\n\n\nCalling this function with positive input skips the code under the if statement, keeping the number positive:\n\nmake_positive(2)\n\n2\n\n\nSometimes we need an else statement as well:\n\ndef add_or_subtract(operation, x, y):\n if operation == 'add':\n return x + y\n else:\n return x - y\n\nThis triggers the code under the if statement:\n\nadd_or_subtract('add', 1, 2)\n\n3\n\n\nSince the Boolean check in the if statement was False, this triggers the code under the else statement:\n\nadd_or_subtract('subtract', 1, 2)\n\n-1\n\n\nFor more complicated logic, we can also use elif. We can have any number of elif statements. Optionally, we can include else.\n\ndef calculate(operation, x, y):\n if operation == 'add':\n return x + y\n elif operation == 'subtract':\n return x - y\n elif operation == 'multiply':\n return x * y\n elif operation == 'division':\n return x / y\n else:\n print(\"This case hasn't been handled\")\n\nThe code keeps checking the conditions in the if statements from top to bottom until it finds multiply:\n\ncalculate('multiply', 3, 4)\n\n12\n\n\nThe code keeps checking the conditions in the if statements from top to bottom until it hits the else statement:\n\ncalculate('power', 3, 4)\n\nThis case hasn't been handled", + "crumbs": [ + "Home", + "Python 101" + ] + }, + { + "objectID": "notebooks/python_101.html#loops", + "href": "notebooks/python_101.html#loops", + "title": "Python 101", + "section": "", + "text": "With while loops, we can keep running code until some stopping condition is met:\n\ndone = False\nvalue = 2\nwhile not done:\n print('Still going...', value)\n value *= 2\n if value > 10:\n done = True\n\nStill going... 2\nStill going... 4\nStill going... 8\n\n\nNote this can also be written as, by moving the condition to the while statement:\n\nvalue = 2\nwhile value < 10:\n print('Still going...', value)\n value *= 2\n\nStill going... 2\nStill going... 4\nStill going... 8\n\n\n\n\n\nWith for loops, we can run our code for each element in a collection:\n\nfor i in range(5):\n print(i)\n\n0\n1\n2\n3\n4\n\n\nWe can use for loops with lists, tuples, sets, and dictionaries as well:\n\nfor element in my_list:\n print(element)\n\nhello\n3.8\nTrue\nPython\n\n\n\nfor key, value in shopping_list.items():\n print('For', key, 'we need to buy', value)\n\nFor veggies we need to buy ['spinach', 'kale', 'beets']\nFor fruits we need to buy bananas\nFor meat we need to buy 0\n\n\nWith for loops, we don’t have to worry about checking if we have reached the stopping condition. Conversely, while loops can cause infinite loops if we don’t remember to update variables.", + "crumbs": [ + "Home", + "Python 101" + ] + }, + { + "objectID": "notebooks/python_101.html#imports", + "href": "notebooks/python_101.html#imports", + "title": "Python 101", + "section": "", + "text": "We have been working with the portion of Python that is available without importing additional functionality. The Python standard library that comes with the install of Python is broken up into several modules, but we often only need a few. We can import whatever we need: a module in the standard library, a 3rd-party library, or code that we wrote. This is done with an import statement:\n\nimport math\n\nprint(math.pi)\n\n3.141592653589793\n\n\nIf we only need a small piece from that module, we can do the following instead:\n\nfrom math import pi\n\nprint(pi)\n\n3.141592653589793\n\n\nWarning: anything you import is added to the namespace, so if you create a new variable/function/etc. with the same name it will overwrite the previous value. For this reason, we have to be careful with variable names e.g. if you name something sum, you won’t be able to add using the sum() built-in function anymore. Using notebooks or an IDE will help you avoid these issues with syntax highlighting.", + "crumbs": [ + "Home", + "Python 101" + ] + }, + { + "objectID": "notebooks/python_101.html#installing-3rd-party-packages", + "href": "notebooks/python_101.html#installing-3rd-party-packages", + "title": "Python 101", + "section": "", + "text": "We can use pip or conda to install packages, depending on how we created our virtual environment. We will walk through the commands to create virtual environments with conda. The environment MUST be activated before installing the packages for this text; otherwise, it’s possible they interfere with other projects on your machine or vice versa.\nTo install a package, we can use conda install <package_name> to download a package from the default conda channel. Optionally, we can provide a specific version to install conda install pandas==0.23.4. Even further, can define which channel that we install a package from for example we can install a package from the conda-forge channel by with conda install -c conda-forge pandas=0.23.4. Without that specification, we will get the most stable version. When we have many packages to install we will typically use a environment.yml or requirements.txt file: conda env update -f environment.yml from within your active environment or conda env update -n ENVNAME -f environment.yml if you are updating an update you are not actively in.\nNote: running conda env export ENVNAME > environment.yml will send the list of platform-specific packages installed in the activate environment and their respective versions to the environment.yml file.", + "crumbs": [ + "Home", + "Python 101" + ] + }, + { + "objectID": "notebooks/python_101.html#classes", + "href": "notebooks/python_101.html#classes", + "title": "Python 101", + "section": "", + "text": "So far we have used Python as a functional programming language, but we also have the option to use it for object-oriented programming. You can think of a class as a way to group similar functionality together. Let’s create a calculator class which can handle mathematical operations for us. For this, we use the class keyword and define methods for taking actions on the calculator. These methods are functions that take self as the first argument. When calling them, we don’t pass in anything for that argument (example after this):\n\nclass Calculator:\n \"\"\"This is the class docstring.\"\"\"\n \n def __init__(self):\n \"\"\"This is a method and it is called when we create an object of type `Calculator`.\"\"\"\n self.on = False\n \n def turn_on(self):\n \"\"\"This method turns on the calculator.\"\"\"\n self.on = True\n \n def add(self, x, y):\n \"\"\"Perform addition if calculator is on\"\"\"\n if self.on:\n return x + y\n else:\n print('the calculator is not on')\n\nIn order to use the calculator, we need to instantiate an instance or object of type Calculator. Since the __init__() method has no parameters other than self, we don’t need to provide anything:\n\nmy_calculator = Calculator()\n\nLet’s try to add some numbers:\n\nmy_calculator.add(1, 2)\n\nthe calculator is not on\n\n\nOops!! The calculator is not on. Let’s turn it on:\n\nmy_calculator.turn_on()\n\nLet’s try again:\n\nmy_calculator.add(1, 2)\n\n3\n\n\nWe can access attributes on object with dot notation. In this example, the only attribute is on, and it is set in the __init__() method:\n\nmy_calculator.on\n\nTrue\n\n\nNote that we can also update attributes:\n\nmy_calculator.on = False\nmy_calculator.add(1, 2)\n\nthe calculator is not on\n\n\nFinally, we can use help() to get more information on the object:\n\nhelp(my_calculator)\n\nHelp on Calculator in module __main__ object:\n\nclass Calculator(builtins.object)\n | This is the class docstring.\n | \n | Methods defined here:\n | \n | __init__(self)\n | This is a method and it is called when we create an object of type `Calculator`.\n | \n | add(self, x, y)\n | Perform addition if calculator is on\n | \n | turn_on(self)\n | This method turns on the calculator.\n | \n | ----------------------------------------------------------------------\n | Data descriptors defined here:\n | \n | __dict__\n | dictionary for instance variables (if defined)\n | \n | __weakref__\n | list of weak references to the object (if defined)\n\n\n\n… and also for a method:\n\nhelp(my_calculator.add)\n\nHelp on method add in module __main__:\n\nadd(x, y) method of __main__.Calculator instance\n Perform addition if calculator is on", + "crumbs": [ + "Home", + "Python 101" + ] + }, + { + "objectID": "notebooks/python_101.html#next-steps", + "href": "notebooks/python_101.html#next-steps", + "title": "Python 101", + "section": "", + "text": "This was a crash course in Python. This isn’t an exhaustive list of all of the features available to you.", + "crumbs": [ + "Home", + "Python 101" + ] + }, + { + "objectID": "notebooks/pandas_plotting_module.html", + "href": "notebooks/pandas_plotting_module.html", + "title": "The pandas.plotting module", + "section": "", + "text": "Pandas provides some extra plotting functions for some new plot types.\n\n\nIn this notebook, we will be working with Facebook’s stock price throughout 2018 (obtained using the stock_analysis package).\n\n\n\n\n%matplotlib inline\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)\n\n\n\n\nEasily create scatter plots between all columns in the dataset:\n\nfrom pandas.plotting import scatter_matrix\nscatter_matrix(fb, figsize=(10, 10))\n\narray([[<Axes: xlabel='open', ylabel='open'>,\n <Axes: xlabel='high', ylabel='open'>,\n <Axes: xlabel='low', ylabel='open'>,\n <Axes: xlabel='close', ylabel='open'>,\n <Axes: xlabel='volume', ylabel='open'>],\n [<Axes: xlabel='open', ylabel='high'>,\n <Axes: xlabel='high', ylabel='high'>,\n <Axes: xlabel='low', ylabel='high'>,\n <Axes: xlabel='close', ylabel='high'>,\n <Axes: xlabel='volume', ylabel='high'>],\n [<Axes: xlabel='open', ylabel='low'>,\n <Axes: xlabel='high', ylabel='low'>,\n <Axes: xlabel='low', ylabel='low'>,\n <Axes: xlabel='close', ylabel='low'>,\n <Axes: xlabel='volume', ylabel='low'>],\n [<Axes: xlabel='open', ylabel='close'>,\n <Axes: xlabel='high', ylabel='close'>,\n <Axes: xlabel='low', ylabel='close'>,\n <Axes: xlabel='close', ylabel='close'>,\n <Axes: xlabel='volume', ylabel='close'>],\n [<Axes: xlabel='open', ylabel='volume'>,\n <Axes: xlabel='high', ylabel='volume'>,\n <Axes: xlabel='low', ylabel='volume'>,\n <Axes: xlabel='close', ylabel='volume'>,\n <Axes: xlabel='volume', ylabel='volume'>]], dtype=object)\n\n\n\n\n\n\n\n\n\nChanging the diagonal from histograms to KDE:\n\nscatter_matrix(fb, figsize=(10, 10), diagonal='kde')\n\narray([[<Axes: xlabel='open', ylabel='open'>,\n <Axes: xlabel='high', ylabel='open'>,\n <Axes: xlabel='low', ylabel='open'>,\n <Axes: xlabel='close', ylabel='open'>,\n <Axes: xlabel='volume', ylabel='open'>],\n [<Axes: xlabel='open', ylabel='high'>,\n <Axes: xlabel='high', ylabel='high'>,\n <Axes: xlabel='low', ylabel='high'>,\n <Axes: xlabel='close', ylabel='high'>,\n <Axes: xlabel='volume', ylabel='high'>],\n [<Axes: xlabel='open', ylabel='low'>,\n <Axes: xlabel='high', ylabel='low'>,\n <Axes: xlabel='low', ylabel='low'>,\n <Axes: xlabel='close', ylabel='low'>,\n <Axes: xlabel='volume', ylabel='low'>],\n [<Axes: xlabel='open', ylabel='close'>,\n <Axes: xlabel='high', ylabel='close'>,\n <Axes: xlabel='low', ylabel='close'>,\n <Axes: xlabel='close', ylabel='close'>,\n <Axes: xlabel='volume', ylabel='close'>],\n [<Axes: xlabel='open', ylabel='volume'>,\n <Axes: xlabel='high', ylabel='volume'>,\n <Axes: xlabel='low', ylabel='volume'>,\n <Axes: xlabel='close', ylabel='volume'>,\n <Axes: xlabel='volume', ylabel='volume'>]], dtype=object)\n\n\n\n\n\n\n\n\n\n\n\n\nLag plots let us see how the variable correlates with past observations of itself. Random data has no pattern:\n\nfrom pandas.plotting import lag_plot\nnp.random.seed(0) # make this repeatable\nlag_plot(pd.Series(np.random.random(size=200)))\n\n\n\n\n\n\n\n\nData with some level of correlation to itself (autocorrelation) may have patterns. Stock prices are highly autocorrelated:\n\nlag_plot(fb.close)\n\n\n\n\n\n\n\n\nThe default lag is 1, but we can alter this with the lag parameter. Let’s look at a 5 day lag (a week of trading activity):\n\nlag_plot(fb.close, lag=5)\n\n\n\n\n\n\n\n\n\n\n\nWe can use the autocorrelation plot to see if this relationship may be meaningful or is just noise. Random data will not have any significant autocorrelation (it stays within the bounds below):\n\nfrom pandas.plotting import autocorrelation_plot\nnp.random.seed(0) # make this repeatable\nautocorrelation_plot(pd.Series(np.random.random(size=200)))\n\n\n\n\n\n\n\n\nStock data, on the other hand, does have significant autocorrelation:\n\nautocorrelation_plot(fb.close)\n\n\n\n\n\n\n\n\n\n\n\nThis plot helps us understand the uncertainty in our summary statistics:\n\nfrom pandas.plotting import bootstrap_plot\nfig = bootstrap_plot(fb.volume, fig=plt.figure(figsize=(10, 6)))", + "crumbs": [ + "Home", + "The `pandas.plotting` module" + ] + }, + { + "objectID": "notebooks/pandas_plotting_module.html#about-the-data", + "href": "notebooks/pandas_plotting_module.html#about-the-data", + "title": "The pandas.plotting module", + "section": "", + "text": "In this notebook, we will be working with Facebook’s stock price throughout 2018 (obtained using the stock_analysis package).", + "crumbs": [ + "Home", + "The `pandas.plotting` module" + ] + }, + { + "objectID": "notebooks/pandas_plotting_module.html#setup", + "href": "notebooks/pandas_plotting_module.html#setup", + "title": "The pandas.plotting module", + "section": "", + "text": "%matplotlib inline\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)", + "crumbs": [ + "Home", + "The `pandas.plotting` module" + ] + }, + { + "objectID": "notebooks/pandas_plotting_module.html#scatter-matrix", + "href": "notebooks/pandas_plotting_module.html#scatter-matrix", + "title": "The pandas.plotting module", + "section": "", + "text": "Easily create scatter plots between all columns in the dataset:\n\nfrom pandas.plotting import scatter_matrix\nscatter_matrix(fb, figsize=(10, 10))\n\narray([[<Axes: xlabel='open', ylabel='open'>,\n <Axes: xlabel='high', ylabel='open'>,\n <Axes: xlabel='low', ylabel='open'>,\n <Axes: xlabel='close', ylabel='open'>,\n <Axes: xlabel='volume', ylabel='open'>],\n [<Axes: xlabel='open', ylabel='high'>,\n <Axes: xlabel='high', ylabel='high'>,\n <Axes: xlabel='low', ylabel='high'>,\n <Axes: xlabel='close', ylabel='high'>,\n <Axes: xlabel='volume', ylabel='high'>],\n [<Axes: xlabel='open', ylabel='low'>,\n <Axes: xlabel='high', ylabel='low'>,\n <Axes: xlabel='low', ylabel='low'>,\n <Axes: xlabel='close', ylabel='low'>,\n <Axes: xlabel='volume', ylabel='low'>],\n [<Axes: xlabel='open', ylabel='close'>,\n <Axes: xlabel='high', ylabel='close'>,\n <Axes: xlabel='low', ylabel='close'>,\n <Axes: xlabel='close', ylabel='close'>,\n <Axes: xlabel='volume', ylabel='close'>],\n [<Axes: xlabel='open', ylabel='volume'>,\n <Axes: xlabel='high', ylabel='volume'>,\n <Axes: xlabel='low', ylabel='volume'>,\n <Axes: xlabel='close', ylabel='volume'>,\n <Axes: xlabel='volume', ylabel='volume'>]], dtype=object)\n\n\n\n\n\n\n\n\n\nChanging the diagonal from histograms to KDE:\n\nscatter_matrix(fb, figsize=(10, 10), diagonal='kde')\n\narray([[<Axes: xlabel='open', ylabel='open'>,\n <Axes: xlabel='high', ylabel='open'>,\n <Axes: xlabel='low', ylabel='open'>,\n <Axes: xlabel='close', ylabel='open'>,\n <Axes: xlabel='volume', ylabel='open'>],\n [<Axes: xlabel='open', ylabel='high'>,\n <Axes: xlabel='high', ylabel='high'>,\n <Axes: xlabel='low', ylabel='high'>,\n <Axes: xlabel='close', ylabel='high'>,\n <Axes: xlabel='volume', ylabel='high'>],\n [<Axes: xlabel='open', ylabel='low'>,\n <Axes: xlabel='high', ylabel='low'>,\n <Axes: xlabel='low', ylabel='low'>,\n <Axes: xlabel='close', ylabel='low'>,\n <Axes: xlabel='volume', ylabel='low'>],\n [<Axes: xlabel='open', ylabel='close'>,\n <Axes: xlabel='high', ylabel='close'>,\n <Axes: xlabel='low', ylabel='close'>,\n <Axes: xlabel='close', ylabel='close'>,\n <Axes: xlabel='volume', ylabel='close'>],\n [<Axes: xlabel='open', ylabel='volume'>,\n <Axes: xlabel='high', ylabel='volume'>,\n <Axes: xlabel='low', ylabel='volume'>,\n <Axes: xlabel='close', ylabel='volume'>,\n <Axes: xlabel='volume', ylabel='volume'>]], dtype=object)", + "crumbs": [ + "Home", + "The `pandas.plotting` module" + ] + }, + { + "objectID": "notebooks/pandas_plotting_module.html#lag-plot", + "href": "notebooks/pandas_plotting_module.html#lag-plot", + "title": "The pandas.plotting module", + "section": "", + "text": "Lag plots let us see how the variable correlates with past observations of itself. Random data has no pattern:\n\nfrom pandas.plotting import lag_plot\nnp.random.seed(0) # make this repeatable\nlag_plot(pd.Series(np.random.random(size=200)))\n\n\n\n\n\n\n\n\nData with some level of correlation to itself (autocorrelation) may have patterns. Stock prices are highly autocorrelated:\n\nlag_plot(fb.close)\n\n\n\n\n\n\n\n\nThe default lag is 1, but we can alter this with the lag parameter. Let’s look at a 5 day lag (a week of trading activity):\n\nlag_plot(fb.close, lag=5)", + "crumbs": [ + "Home", + "The `pandas.plotting` module" + ] + }, + { + "objectID": "notebooks/pandas_plotting_module.html#autocorrelation-plots", + "href": "notebooks/pandas_plotting_module.html#autocorrelation-plots", + "title": "The pandas.plotting module", + "section": "", + "text": "We can use the autocorrelation plot to see if this relationship may be meaningful or is just noise. Random data will not have any significant autocorrelation (it stays within the bounds below):\n\nfrom pandas.plotting import autocorrelation_plot\nnp.random.seed(0) # make this repeatable\nautocorrelation_plot(pd.Series(np.random.random(size=200)))\n\n\n\n\n\n\n\n\nStock data, on the other hand, does have significant autocorrelation:\n\nautocorrelation_plot(fb.close)", + "crumbs": [ + "Home", + "The `pandas.plotting` module" + ] + }, + { + "objectID": "notebooks/pandas_plotting_module.html#bootstrap-plot", + "href": "notebooks/pandas_plotting_module.html#bootstrap-plot", + "title": "The pandas.plotting module", + "section": "", + "text": "This plot helps us understand the uncertainty in our summary statistics:\n\nfrom pandas.plotting import bootstrap_plot\nfig = bootstrap_plot(fb.volume, fig=plt.figure(figsize=(10, 6)))", + "crumbs": [ + "Home", + "The `pandas.plotting` module" + ] + }, + { + "objectID": "notebooks/making_dataframes_from_api_requests.html", + "href": "notebooks/making_dataframes_from_api_requests.html", + "title": "Making Pandas DataFrames from API Requests", + "section": "", + "text": "Making Pandas DataFrames from API Requests\nIn this example, we will use the U.S. Geological Survey’s API to grab a JSON object of earthquake data and convert it to a pandas.DataFrame.\nUSGS API: https://earthquake.usgs.gov/fdsnws/event/1/\n\nGet Data from API\n\nimport datetime as dt\nimport pandas as pd\nimport requests\n\nyesterday = dt.date.today() - dt.timedelta(days=1)\napi = 'https://earthquake.usgs.gov/fdsnws/event/1/query'\npayload = {\n 'format': 'geojson',\n 'starttime': yesterday - dt.timedelta(days=30),\n 'endtime': yesterday\n}\nresponse = requests.get(api, params=payload)\n\n# let's make sure the request was OK\nresponse.status_code\n\n200\n\n\nResponse of 200 means OK, so we can pull the data out of the result. Since we asked the API for a JSON payload, we can extract it from the response with the json() method.\n\n\nIsolate the Data from the JSON Response\nWe need to check the structures of the response data to know where our data is.\n\nearthquake_json = response.json()\nearthquake_json.keys()\n\ndict_keys(['type', 'metadata', 'features', 'bbox'])\n\n\nThe USGS API provides information about our request in the metadata key. Note that your result will be different, regardless of the date range you chose, because the API includes a timestamp for when the data was pulled:\n\nearthquake_json['metadata']\n\n{'generated': 1686247399000,\n 'url': 'https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2023-05-08&endtime=2023-06-07',\n 'title': 'USGS Earthquakes',\n 'status': 200,\n 'api': '1.14.0',\n 'count': 11706}\n\n\nEach element in the JSON array features is a row of data for our dataframe.\n\ntype(earthquake_json['features'])\n\nlist\n\n\nYour data will be different depending on the date you run this.\n\nearthquake_json['features'][0]\n\n{'type': 'Feature',\n 'properties': {'mag': 0.5,\n 'place': '17km SE of Anza, CA',\n 'time': 1686095356120,\n 'updated': 1686178472172,\n 'tz': None,\n 'url': 'https://earthquake.usgs.gov/earthquakes/eventpage/ci40479680',\n 'detail': 'https://earthquake.usgs.gov/fdsnws/event/1/query?eventid=ci40479680&format=geojson',\n 'felt': None,\n 'cdi': None,\n 'mmi': None,\n 'alert': None,\n 'status': 'reviewed',\n 'tsunami': 0,\n 'sig': 4,\n 'net': 'ci',\n 'code': '40479680',\n 'ids': ',ci40479680,',\n 'sources': ',ci,',\n 'types': ',nearby-cities,origin,phase-data,scitech-link,',\n 'nst': 21,\n 'dmin': 0.07016,\n 'rms': 0.26,\n 'gap': 86,\n 'magType': 'ml',\n 'type': 'earthquake',\n 'title': 'M 0.5 - 17km SE of Anza, CA'},\n 'geometry': {'type': 'Point', 'coordinates': [-116.5405, 33.447, 11.37]},\n 'id': 'ci40479680'}\n\n\n\n\nConvert to DataFrame\nWe need to grab the properties section out of every entry in the features JSON array to create our dataframe.\n\nearthquake_properties_data = [\n quake['properties'] for quake in earthquake_json['features']\n]\ndf = pd.DataFrame(earthquake_properties_data)\ndf.head()\n\n\n\n\n\n\n\n\nmag\nplace\ntime\nupdated\ntz\nurl\ndetail\nfelt\ncdi\nmmi\n...\nids\nsources\ntypes\nnst\ndmin\nrms\ngap\nmagType\ntype\ntitle\n\n\n\n\n0\n0.50\n17km SE of Anza, CA\n1686095356120\n1686178472172\nNone\nhttps://earthquake.usgs.gov/earthquakes/eventp...\nhttps://earthquake.usgs.gov/fdsnws/event/1/que...\nNaN\nNaN\nNaN\n...\n,ci40479680,\n,ci,\n,nearby-cities,origin,phase-data,scitech-link,\n21.0\n0.070160\n0.26\n86.0\nml\nearthquake\nM 0.5 - 17km SE of Anza, CA\n\n\n1\n1.24\n6 km W of Blanchard, Oklahoma\n1686095288900\n1686140204481\nNone\nhttps://earthquake.usgs.gov/earthquakes/eventp...\nhttps://earthquake.usgs.gov/fdsnws/event/1/que...\n0.0\n1.0\nNaN\n...\n,ok2023lavp,\n,ok,\n,dyfi,origin,phase-data,\n64.0\n0.089083\n0.44\n42.0\nml\nearthquake\nM 1.2 - 6 km W of Blanchard, Oklahoma\n\n\n2\n5.20\nsoutheast of the Loyalty Islands\n1686094693572\n1686095577040\nNone\nhttps://earthquake.usgs.gov/earthquakes/eventp...\nhttps://earthquake.usgs.gov/fdsnws/event/1/que...\nNaN\nNaN\nNaN\n...\n,us7000k6rg,\n,us,\n,origin,phase-data,\n32.0\n2.755000\n0.82\n85.0\nmb\nearthquake\nM 5.2 - southeast of the Loyalty Islands\n\n\n3\n0.24\n10 km NNE of Government Camp, Oregon\n1686094611590\n1686118209350\nNone\nhttps://earthquake.usgs.gov/earthquakes/eventp...\nhttps://earthquake.usgs.gov/fdsnws/event/1/que...\nNaN\nNaN\nNaN\n...\n,uw61931036,\n,uw,\n,origin,phase-data,\n3.0\n0.032620\n0.04\n239.0\nml\nearthquake\nM 0.2 - 10 km NNE of Government Camp, Oregon\n\n\n4\n-0.01\n8 km NNE of Government Camp, Oregon\n1686094117310\n1686117953180\nNone\nhttps://earthquake.usgs.gov/earthquakes/eventp...\nhttps://earthquake.usgs.gov/fdsnws/event/1/que...\nNaN\nNaN\nNaN\n...\n,uw61931031,\n,uw,\n,origin,phase-data,\n4.0\n0.011510\n0.04\n173.0\nml\nearthquake\nM 0.0 - 8 km NNE of Government Camp, Oregon\n\n\n\n\n5 rows × 26 columns\n\n\n\n\n\n(Optional) Write Data to CSV\n\ndf.to_csv('earthquakes.csv', index=False)", + "crumbs": [ + "Home", + "Making Pandas DataFrames from API Requests" + ] + }, + { + "objectID": "notebooks/introduction_to_data_analysis.html", + "href": "notebooks/introduction_to_data_analysis.html", + "title": "Introduction to Data Analysis", + "section": "", + "text": "This notebook serves as a summary of the fundamentals. For a Python crash-course/refresher, work through the python_101.ipynb notebook.\n\n\n\nimport sys\nsys.path.append('../src/')\nsys.dont_write_bytecode = True\n\nimport stats_viz\n\n\n\n\nWhen conducting a data analysis, we will move back and forth between four main processes:\n\nData Collection: Every analysis starts with collecting data. We can collect data from a variety of sources, including databases, APIs, flat files, and the Internet.\nData Wrangling: After we have our data, we need to prepare it for our analysis. This may involve reshaping it, changing data types, handling missing values, and/or aggregating it.\nExploratory Data Analysis (EDA): We can use visualizations to explore our data and summarize it. During this time, we will also begin exploring the data by looking at its structure, format, and summary statistics.\nDrawing Conclusions: After we have thoroughly explored our data, we can try to draw conclusions or model it.\n\n\n\n\nAs this is an overview of statistics, we will discuss some concepts. By no means is this exhaustive.\n\n\nSome resampling (sampling from the sample) techniques you will see: - simple random sampling: pick with a random number generator - stratified random sampling: randomly pick preserving the proportion of groups in the data - bootstrapping: sampling with replacement (more info: YouTube video and Wikipedia article)\n\n\n\nWe use descriptive statistics to describe the data. The data we work with is usually a sample taken from the population. The statistics we will discuss here are referred to as sample statistics because they are calculated on the sample and can be used as estimators for the population parameters.\n\n\nThree common ways to describe the central tendency of a distribution are mean, median, and mode. ##### Mean The sample mean is an estimator for the population mean (\\(\\mu\\)) and is defined as:\n\\[\\bar{x} = \\frac{\\sum_{1}^{n} x_i}{n}\\] ##### Median The median represents the 50th percentile of our data; this means that 50% of the values are greater than the median and 50% are less than the median. It is calculated by taking the middle value from an ordered list of values.\n\n\nThe mode is the most common value in the data. We can use it to describe categorical data or, for continuous data, the shape of the distribution:\n\nax = stats_viz.different_modal_plots()\n\n\n\n\n\n\n\n\n\n\n\n\nMeasures of spread tell us how the data is dispersed; this will indicate how thin (low dispersion) or wide (very spread out) our distribution is.\n\n\nThe range is the distance between the smallest value (minimum) and the largest value (maximum):\n\\[range = max(X) - min(X)\\]\n\n\n\nThe variance describes how far apart observations are spread out from their average value (the mean). When calculating the sample variance, we divide by n - 1 instead of n to account for using the sample mean (\\(\\bar{x}\\)):\n\\[s^2 = \\frac{\\sum_{1}^{n} (x_i - \\bar{x})^2}{n - 1}\\]\nThis is referred to as Bessel’s correction and is applied to get an unbiased estimator of the population variance.\nNote that this will be in units-squared of whatever was being measured.\n\n\n\nThe standard deviation is the square root of the variance, giving us a measure in the same units as our data. The sample standard deviation is calculated as follows:\n\\[s = \\sqrt{\\frac{\\sum_{1}^{n} (x_i - \\bar{x})^2}{n - 1}} = \\sqrt{s^2}\\]\n\nax = stats_viz.effect_of_std_dev()\n\n\n\n\n\n\n\n\nNote that \\(\\sigma^2\\) is the population variance and \\(\\sigma\\) is the population standard deviation.\n\n\n\nThe coefficient of variation (CV) gives us a unitless ratio of the standard deviation to the mean. Since, it has no units we can compare dispersion across datasets:\n\\[CV = \\frac{s}{\\bar{x}}\\]\n\n\n\nThe interquartile range (IQR) gives us the spread of data around the median and quantifies how much dispersion we have in the middle 50% of our distribution:\n\\[IQR = Q_3 - Q_1\\]\n\n\n\nThe quartile coefficient of dispersion also is a unitless statistic for comparing datasets. However, it uses the median as the measure of center. It is calculated by dividing the semi-quartile range (half the IQR) by the midhinge (midpoint between the first and third quartiles):\n\\[QCD = \\frac{\\frac{Q_3 - Q_1}{2}}{\\frac{Q_1 + Q_3}{2}} = \\frac{Q_3 - Q_1}{Q_3 + Q_1}\\]\n\n\n\n\nThe 5-number summary provides 5 descriptive statistics that summarize our data:\n\n\n\n\nQuartile\nStatistic\nPercentile\n\n\n\n\n1.\n\\(Q_0\\)\nminimum\n\\(0^{th}\\)\n\n\n2.\n\\(Q_1\\)\nN/A\n\\(25^{th}\\)\n\n\n3.\n\\(Q_2\\)\nmedian\n\\(50^{th}\\)\n\n\n4.\n\\(Q_3\\)\nN/A\n\\(75^{th}\\)\n\n\n5.\n\\(Q_4\\)\nmaximum\n\\(100^{th}\\)\n\n\n\nThis summary can be visualized using a box plot (also called box-and-whisker plot). The box has an upper bound of \\(Q_3\\) and a lower bound of \\(Q_1\\). The median will be a line somewhere in this box. The whiskers extend from the box towards the minimum/maximum. For our purposes, they will extend to \\(Q_3 + 1.5 \\times IQR\\) and \\(Q_1 - 1.5 \\times IQR\\) and anything beyond will be represented as individual points for outliers:\n\nax = stats_viz.example_boxplot()\n\n\n\n\n\n\n\n\nThe box plot doesn’t show us how the data is distributed within the quartiles. To get a better sense of the distribution, we can use a histogram, which will show us the amount of observations that fall into equal-width bins. We can vary the number of bins to use, but be aware that this can change our impression of what the distribution appears to be:\n\nax = stats_viz.example_histogram()\n\n\n\n\n\n\n\n\nWe can also visualize the distribution using a kernel density estimate (KDE). This will estimate the probability density function (PDF). This function shows how probability is distributed over the values. Higher values of the PDF mean higher likelihoods:\n\nax = stats_viz.example_kde()\n\n\n\n\n\n\n\n\nNote that both the KDE and histogram estimate the distribution:\n\nax = stats_viz.hist_and_kde()\n\n\n\n\n\n\n\n\nSkewed distributions have more observations on one side. The mean will be less than the median with negative skew, while the opposite is true of positive skew:\n\nax = stats_viz.skew_examples()\n\n\n\n\n\n\n\n\nWe can use the cumulative distribution function (CDF) to find probabilities of getting values within a certain range. The CDF is the integral of the PDF:\n\\[CDF = F(x) = \\int_{-\\infty}^{x} f(t) dt\\]\nNote that \\(f(t)\\) is the PDF and \\(\\int_{-\\infty}^{\\infty} f(t) dt = 1\\).\nThe probability of the random variable \\(X\\) being less than or equal to the specific value of \\(x\\) is denoted as \\(P(X ≤ x)\\). Note that for a continuous random variable the probability of it being exactly \\(x\\) is zero.\nLet’s look at the estimate of the CDF from the sample data we used for the box plot, called the empirical cumulative distribution function (ECDF):\n\nax = stats_viz.cdf_example()\n\n\n\n\n\n\n\n\nWe can find any range we want if we use some algebra as in the rightmost subplot above.\n\n\n\n\nGaussian (normal) distribution: looks like a bell curve and is parameterized by its mean (μ) and standard deviation (σ). Many things in nature happen to follow the normal distribution, like heights. Note that testing if a distribution is normal is not trivial. Written as \\(N(\\mu, \\sigma)\\).\nPoisson distribution: discrete distribution that is often used to model arrivals. Parameterized by its mean, lambda (λ). Written as \\(Pois(\\lambda)\\).\nExponential distribution: can be used to model the time between arrivals. Parameterized by its mean, lambda (λ). Written as \\(Exp(\\lambda)\\).\nUniform distribution: places equal likelihood on each value within its bounds (a and b). We often use this for random number generation. Written as \\(U(a, b)\\).\nBernoulli distribution: When we pick a random number to simulate a single success/failure outcome, it is called a Bernoulli trial. This is parameterized by the probability of success (p). Written as \\(Bernoulli(p)\\).\nBinomial distribution: When we run the same experiment n times, the total number of successes is then a binomial random variable. Written as \\(B(n, p)\\).\n\nWe can visualize both discrete and continuous distributions; however, discrete distributions give us a probability mass function (PMF) instead of a PDF:\n\nax = stats_viz.common_dists()\n\n\n\n\n\n\n\n\n\n\n\nIn order to compare variables from different distributions, we would have to scale the data, which we could do with the range by using min-max scaling:\n\\[x_{scaled}=\\frac{x - min(X)}{range(X)}\\]\nAnother way is to use a Z-score to standardize the data:\n\\[z_i = \\frac{x_i - \\bar{x}}{s}\\]\n\n\n\nThe covariance is a statistic for quantifying the relationship between variables by showing how one variable changes with respect to another (also referred to as their joint variance):\n\\[cov(X, Y) = E[(X-E[X])(Y-E[Y])]\\]\nE[X] is the expectation of the random variable X (its long-run average).\nThe sign of the covariance gives us the direction of the relationship, but we need the magnitude as well. For that, we calculate the Pearson correlation coefficient (\\(\\rho\\)):\n\\[\\rho_{X, Y} = \\frac{cov(X, Y)}{s_X s_Y}\\]\nExamples:\n\nax = stats_viz.correlation_coefficient_examples()\n\n\n\n\n\n\n\n\nFrom left to right: no correlation, weak negative correlation, strong positive correlation, and nearly perfect negative correlation.\nOften, it is more informative to use scatter plots to check for relationships between variables. This is because the correlation may be strong, but the relationship may not be linear:\n\nax = stats_viz.non_linear_relationships()\n\n\n\n\n\n\n\n\nRemember, correlation does not imply causation. While we may find a correlation between X and Y, it does not mean that X causes Y or Y causes X. It is possible there is some Z that causes both or that X causes some intermediary event that causes Y — it could even be a coincidence. Be sure to check out Tyler Vigen’s Spurious Correlations blog for some interesting correlations.\n\n\n\nNot only can our correlation coefficients be misleading, but so can summary statistics. Anscombe’s quartet is a collection of four different datasets that have identical summary statistics and correlation coefficients, however, when plotted, it is obvious they are not similar:\n\nax = stats_viz.anscombes_quartet()\n\n\n\n\n\n\n\n\nAnother example of this is the Datasaurus Dozen:\n\nax = stats_viz.datasaurus_dozen()\n\n\n\n\n\n\n\n\n\n\n\n\nSay our favorite ice cream shop has asked us to help predict how many ice creams they can expect to sell on a given day. They are convinced that the temperature outside has strong influence on their sales, so they collected data on the number of ice creams sold at a given temperature. We agree to help them, and the first thing we do is make a scatter plot of the data they gave us:\n\nax = stats_viz.example_scatter_plot()\n\n\n\n\n\n\n\n\nWe can observe an upward trend in the scatter plot: more ice creams are sold at higher temperatures. In order to help out the ice cream shop, though, we need to find a way to make predictions from this data. We can use a technique called regression to model the relationship between temperature and ice cream sales with an equation:\n\nax = stats_viz.example_regression()\n\n\n\n\n\n\n\n\nWe can use the resulting equation to make predictions for the number of ice creams sold at various temperatures. However, we must keep in mind if we are interpolating or extrapolating. If the temperature value we are using for prediction is within the range of the original data we used to build our regression model, then we are interpolating (solid portion of the red line). On the other hand, if the temperature is beyond the values in the original data, we are extrapolating, which is very dangerous, since we can’t assume the pattern continues indefinitely in each direction (dotted portion of the line). Extremely hot temperatures may cause people to stay inside, meaning no ice creams will be sold, while the equation indicates record-high sales.\nForecasting is a type of prediction for time series. In a process called time series decomposition, time series is decomposed into a trend component, a seasonality component, and a cyclical component. These components can be combined in an additive or multiplicative fashion:\n\nax = stats_viz.time_series_decomposition_example()\n\n\n\n\n\n\n\n\nThe trend component describes the behavior of the time series in the long-term without accounting for the seasonal or cyclical effects. Using the trend, we can make broad statements about the time series in the long-run, such as: the population of Earth is increasing or the value of a stock is stagnating. Seasonality of a time series explains the systematic and calendar-related movements of a time series. For example, the number of ice cream trucks on the streets of New York City is high in the summer and drops to nothing in the winter; this pattern repeats every year regardless of whether the actual amount each summer is the same. Lastly, the cyclical component accounts for anything else unexplained or irregular with the time series; this could be something like a hurricane driving the number of ice cream trucks down in the short-term because it isn’t safe to be outside. This component is difficult to anticipate with a forecast due to its unexpected nature.\nWhen making models to forecast time series, some common methods include ARIMA-family methods and exponential smoothing. ARIMA stands for autoregressive (AR), integrated (I), moving average (MA). Autoregressive models take advantage of the fact that an observation at time \\(t\\) is correlated to a previous observation, for example at time \\(t - 1\\). Note that not all time series are autoregressive. The integrated component concerns the differenced data, or the change in the data from one time to another. Lastly, the moving average component uses a sliding window to average the last \\(x\\) observations where \\(x\\) is the length of the sliding window.\nThe moving average puts equal weight on each time period in the past involved in the calculation. In practice, this isn’t always a realistic expectation of our data. Sometimes all past values are important, but they vary in their influence on future data points. For these cases, we can use exponential smoothing, which allows us to put more weight on more recent values and less weight on values further away from what we are predicting.\n\n\n\nInferential statistics deals with inferring or deducing things from the sample data we have in order to make statements about the population as a whole. Before doing so, we need to know whether we conducted an observational study or an experiment. An observational study can’t be used to determine causation because we can’t control for everything. An experiment on the other hand is controlled.\nRemember that the sample statistics we discussed earlier are estimators for the population parameters. Our estimators need confidence intervals, which provide a point estimate and a margin of error around it. This is the range that the true population parameter will be in at a certain confidence level. At the 95% confidence level, 95% of the confidence intervals calculated from random samples of the population contain the true population parameter.\nWe also have the option of using hypothesis testing. First, we define a null hypothesis (say the true population mean is 0), then we determine a significance level (1 - confidence level), which is the probability of rejecting the null hypothesis when it is true. Our result is statistically significant if the value for the null hypothesis is outside the confidence interval. More info.", + "crumbs": [ + "Home", + "Introduction to Data Analysis" + ] + }, + { + "objectID": "notebooks/introduction_to_data_analysis.html#setup", + "href": "notebooks/introduction_to_data_analysis.html#setup", + "title": "Introduction to Data Analysis", + "section": "", + "text": "import sys\nsys.path.append('../src/')\nsys.dont_write_bytecode = True\n\nimport stats_viz", + "crumbs": [ + "Home", + "Introduction to Data Analysis" + ] + }, + { + "objectID": "notebooks/introduction_to_data_analysis.html#fundamentals-of-data-analysis", + "href": "notebooks/introduction_to_data_analysis.html#fundamentals-of-data-analysis", + "title": "Introduction to Data Analysis", + "section": "", + "text": "When conducting a data analysis, we will move back and forth between four main processes:\n\nData Collection: Every analysis starts with collecting data. We can collect data from a variety of sources, including databases, APIs, flat files, and the Internet.\nData Wrangling: After we have our data, we need to prepare it for our analysis. This may involve reshaping it, changing data types, handling missing values, and/or aggregating it.\nExploratory Data Analysis (EDA): We can use visualizations to explore our data and summarize it. During this time, we will also begin exploring the data by looking at its structure, format, and summary statistics.\nDrawing Conclusions: After we have thoroughly explored our data, we can try to draw conclusions or model it.", + "crumbs": [ + "Home", + "Introduction to Data Analysis" + ] + }, + { + "objectID": "notebooks/introduction_to_data_analysis.html#statistical-foundations", + "href": "notebooks/introduction_to_data_analysis.html#statistical-foundations", + "title": "Introduction to Data Analysis", + "section": "", + "text": "As this is an overview of statistics, we will discuss some concepts. By no means is this exhaustive.\n\n\nSome resampling (sampling from the sample) techniques you will see: - simple random sampling: pick with a random number generator - stratified random sampling: randomly pick preserving the proportion of groups in the data - bootstrapping: sampling with replacement (more info: YouTube video and Wikipedia article)\n\n\n\nWe use descriptive statistics to describe the data. The data we work with is usually a sample taken from the population. The statistics we will discuss here are referred to as sample statistics because they are calculated on the sample and can be used as estimators for the population parameters.\n\n\nThree common ways to describe the central tendency of a distribution are mean, median, and mode. ##### Mean The sample mean is an estimator for the population mean (\\(\\mu\\)) and is defined as:\n\\[\\bar{x} = \\frac{\\sum_{1}^{n} x_i}{n}\\] ##### Median The median represents the 50th percentile of our data; this means that 50% of the values are greater than the median and 50% are less than the median. It is calculated by taking the middle value from an ordered list of values.\n\n\nThe mode is the most common value in the data. We can use it to describe categorical data or, for continuous data, the shape of the distribution:\n\nax = stats_viz.different_modal_plots()\n\n\n\n\n\n\n\n\n\n\n\n\nMeasures of spread tell us how the data is dispersed; this will indicate how thin (low dispersion) or wide (very spread out) our distribution is.\n\n\nThe range is the distance between the smallest value (minimum) and the largest value (maximum):\n\\[range = max(X) - min(X)\\]\n\n\n\nThe variance describes how far apart observations are spread out from their average value (the mean). When calculating the sample variance, we divide by n - 1 instead of n to account for using the sample mean (\\(\\bar{x}\\)):\n\\[s^2 = \\frac{\\sum_{1}^{n} (x_i - \\bar{x})^2}{n - 1}\\]\nThis is referred to as Bessel’s correction and is applied to get an unbiased estimator of the population variance.\nNote that this will be in units-squared of whatever was being measured.\n\n\n\nThe standard deviation is the square root of the variance, giving us a measure in the same units as our data. The sample standard deviation is calculated as follows:\n\\[s = \\sqrt{\\frac{\\sum_{1}^{n} (x_i - \\bar{x})^2}{n - 1}} = \\sqrt{s^2}\\]\n\nax = stats_viz.effect_of_std_dev()\n\n\n\n\n\n\n\n\nNote that \\(\\sigma^2\\) is the population variance and \\(\\sigma\\) is the population standard deviation.\n\n\n\nThe coefficient of variation (CV) gives us a unitless ratio of the standard deviation to the mean. Since, it has no units we can compare dispersion across datasets:\n\\[CV = \\frac{s}{\\bar{x}}\\]\n\n\n\nThe interquartile range (IQR) gives us the spread of data around the median and quantifies how much dispersion we have in the middle 50% of our distribution:\n\\[IQR = Q_3 - Q_1\\]\n\n\n\nThe quartile coefficient of dispersion also is a unitless statistic for comparing datasets. However, it uses the median as the measure of center. It is calculated by dividing the semi-quartile range (half the IQR) by the midhinge (midpoint between the first and third quartiles):\n\\[QCD = \\frac{\\frac{Q_3 - Q_1}{2}}{\\frac{Q_1 + Q_3}{2}} = \\frac{Q_3 - Q_1}{Q_3 + Q_1}\\]\n\n\n\n\nThe 5-number summary provides 5 descriptive statistics that summarize our data:\n\n\n\n\nQuartile\nStatistic\nPercentile\n\n\n\n\n1.\n\\(Q_0\\)\nminimum\n\\(0^{th}\\)\n\n\n2.\n\\(Q_1\\)\nN/A\n\\(25^{th}\\)\n\n\n3.\n\\(Q_2\\)\nmedian\n\\(50^{th}\\)\n\n\n4.\n\\(Q_3\\)\nN/A\n\\(75^{th}\\)\n\n\n5.\n\\(Q_4\\)\nmaximum\n\\(100^{th}\\)\n\n\n\nThis summary can be visualized using a box plot (also called box-and-whisker plot). The box has an upper bound of \\(Q_3\\) and a lower bound of \\(Q_1\\). The median will be a line somewhere in this box. The whiskers extend from the box towards the minimum/maximum. For our purposes, they will extend to \\(Q_3 + 1.5 \\times IQR\\) and \\(Q_1 - 1.5 \\times IQR\\) and anything beyond will be represented as individual points for outliers:\n\nax = stats_viz.example_boxplot()\n\n\n\n\n\n\n\n\nThe box plot doesn’t show us how the data is distributed within the quartiles. To get a better sense of the distribution, we can use a histogram, which will show us the amount of observations that fall into equal-width bins. We can vary the number of bins to use, but be aware that this can change our impression of what the distribution appears to be:\n\nax = stats_viz.example_histogram()\n\n\n\n\n\n\n\n\nWe can also visualize the distribution using a kernel density estimate (KDE). This will estimate the probability density function (PDF). This function shows how probability is distributed over the values. Higher values of the PDF mean higher likelihoods:\n\nax = stats_viz.example_kde()\n\n\n\n\n\n\n\n\nNote that both the KDE and histogram estimate the distribution:\n\nax = stats_viz.hist_and_kde()\n\n\n\n\n\n\n\n\nSkewed distributions have more observations on one side. The mean will be less than the median with negative skew, while the opposite is true of positive skew:\n\nax = stats_viz.skew_examples()\n\n\n\n\n\n\n\n\nWe can use the cumulative distribution function (CDF) to find probabilities of getting values within a certain range. The CDF is the integral of the PDF:\n\\[CDF = F(x) = \\int_{-\\infty}^{x} f(t) dt\\]\nNote that \\(f(t)\\) is the PDF and \\(\\int_{-\\infty}^{\\infty} f(t) dt = 1\\).\nThe probability of the random variable \\(X\\) being less than or equal to the specific value of \\(x\\) is denoted as \\(P(X ≤ x)\\). Note that for a continuous random variable the probability of it being exactly \\(x\\) is zero.\nLet’s look at the estimate of the CDF from the sample data we used for the box plot, called the empirical cumulative distribution function (ECDF):\n\nax = stats_viz.cdf_example()\n\n\n\n\n\n\n\n\nWe can find any range we want if we use some algebra as in the rightmost subplot above.\n\n\n\n\nGaussian (normal) distribution: looks like a bell curve and is parameterized by its mean (μ) and standard deviation (σ). Many things in nature happen to follow the normal distribution, like heights. Note that testing if a distribution is normal is not trivial. Written as \\(N(\\mu, \\sigma)\\).\nPoisson distribution: discrete distribution that is often used to model arrivals. Parameterized by its mean, lambda (λ). Written as \\(Pois(\\lambda)\\).\nExponential distribution: can be used to model the time between arrivals. Parameterized by its mean, lambda (λ). Written as \\(Exp(\\lambda)\\).\nUniform distribution: places equal likelihood on each value within its bounds (a and b). We often use this for random number generation. Written as \\(U(a, b)\\).\nBernoulli distribution: When we pick a random number to simulate a single success/failure outcome, it is called a Bernoulli trial. This is parameterized by the probability of success (p). Written as \\(Bernoulli(p)\\).\nBinomial distribution: When we run the same experiment n times, the total number of successes is then a binomial random variable. Written as \\(B(n, p)\\).\n\nWe can visualize both discrete and continuous distributions; however, discrete distributions give us a probability mass function (PMF) instead of a PDF:\n\nax = stats_viz.common_dists()\n\n\n\n\n\n\n\n\n\n\n\nIn order to compare variables from different distributions, we would have to scale the data, which we could do with the range by using min-max scaling:\n\\[x_{scaled}=\\frac{x - min(X)}{range(X)}\\]\nAnother way is to use a Z-score to standardize the data:\n\\[z_i = \\frac{x_i - \\bar{x}}{s}\\]\n\n\n\nThe covariance is a statistic for quantifying the relationship between variables by showing how one variable changes with respect to another (also referred to as their joint variance):\n\\[cov(X, Y) = E[(X-E[X])(Y-E[Y])]\\]\nE[X] is the expectation of the random variable X (its long-run average).\nThe sign of the covariance gives us the direction of the relationship, but we need the magnitude as well. For that, we calculate the Pearson correlation coefficient (\\(\\rho\\)):\n\\[\\rho_{X, Y} = \\frac{cov(X, Y)}{s_X s_Y}\\]\nExamples:\n\nax = stats_viz.correlation_coefficient_examples()\n\n\n\n\n\n\n\n\nFrom left to right: no correlation, weak negative correlation, strong positive correlation, and nearly perfect negative correlation.\nOften, it is more informative to use scatter plots to check for relationships between variables. This is because the correlation may be strong, but the relationship may not be linear:\n\nax = stats_viz.non_linear_relationships()\n\n\n\n\n\n\n\n\nRemember, correlation does not imply causation. While we may find a correlation between X and Y, it does not mean that X causes Y or Y causes X. It is possible there is some Z that causes both or that X causes some intermediary event that causes Y — it could even be a coincidence. Be sure to check out Tyler Vigen’s Spurious Correlations blog for some interesting correlations.\n\n\n\nNot only can our correlation coefficients be misleading, but so can summary statistics. Anscombe’s quartet is a collection of four different datasets that have identical summary statistics and correlation coefficients, however, when plotted, it is obvious they are not similar:\n\nax = stats_viz.anscombes_quartet()\n\n\n\n\n\n\n\n\nAnother example of this is the Datasaurus Dozen:\n\nax = stats_viz.datasaurus_dozen()\n\n\n\n\n\n\n\n\n\n\n\n\nSay our favorite ice cream shop has asked us to help predict how many ice creams they can expect to sell on a given day. They are convinced that the temperature outside has strong influence on their sales, so they collected data on the number of ice creams sold at a given temperature. We agree to help them, and the first thing we do is make a scatter plot of the data they gave us:\n\nax = stats_viz.example_scatter_plot()\n\n\n\n\n\n\n\n\nWe can observe an upward trend in the scatter plot: more ice creams are sold at higher temperatures. In order to help out the ice cream shop, though, we need to find a way to make predictions from this data. We can use a technique called regression to model the relationship between temperature and ice cream sales with an equation:\n\nax = stats_viz.example_regression()\n\n\n\n\n\n\n\n\nWe can use the resulting equation to make predictions for the number of ice creams sold at various temperatures. However, we must keep in mind if we are interpolating or extrapolating. If the temperature value we are using for prediction is within the range of the original data we used to build our regression model, then we are interpolating (solid portion of the red line). On the other hand, if the temperature is beyond the values in the original data, we are extrapolating, which is very dangerous, since we can’t assume the pattern continues indefinitely in each direction (dotted portion of the line). Extremely hot temperatures may cause people to stay inside, meaning no ice creams will be sold, while the equation indicates record-high sales.\nForecasting is a type of prediction for time series. In a process called time series decomposition, time series is decomposed into a trend component, a seasonality component, and a cyclical component. These components can be combined in an additive or multiplicative fashion:\n\nax = stats_viz.time_series_decomposition_example()\n\n\n\n\n\n\n\n\nThe trend component describes the behavior of the time series in the long-term without accounting for the seasonal or cyclical effects. Using the trend, we can make broad statements about the time series in the long-run, such as: the population of Earth is increasing or the value of a stock is stagnating. Seasonality of a time series explains the systematic and calendar-related movements of a time series. For example, the number of ice cream trucks on the streets of New York City is high in the summer and drops to nothing in the winter; this pattern repeats every year regardless of whether the actual amount each summer is the same. Lastly, the cyclical component accounts for anything else unexplained or irregular with the time series; this could be something like a hurricane driving the number of ice cream trucks down in the short-term because it isn’t safe to be outside. This component is difficult to anticipate with a forecast due to its unexpected nature.\nWhen making models to forecast time series, some common methods include ARIMA-family methods and exponential smoothing. ARIMA stands for autoregressive (AR), integrated (I), moving average (MA). Autoregressive models take advantage of the fact that an observation at time \\(t\\) is correlated to a previous observation, for example at time \\(t - 1\\). Note that not all time series are autoregressive. The integrated component concerns the differenced data, or the change in the data from one time to another. Lastly, the moving average component uses a sliding window to average the last \\(x\\) observations where \\(x\\) is the length of the sliding window.\nThe moving average puts equal weight on each time period in the past involved in the calculation. In practice, this isn’t always a realistic expectation of our data. Sometimes all past values are important, but they vary in their influence on future data points. For these cases, we can use exponential smoothing, which allows us to put more weight on more recent values and less weight on values further away from what we are predicting.\n\n\n\nInferential statistics deals with inferring or deducing things from the sample data we have in order to make statements about the population as a whole. Before doing so, we need to know whether we conducted an observational study or an experiment. An observational study can’t be used to determine causation because we can’t control for everything. An experiment on the other hand is controlled.\nRemember that the sample statistics we discussed earlier are estimators for the population parameters. Our estimators need confidence intervals, which provide a point estimate and a margin of error around it. This is the range that the true population parameter will be in at a certain confidence level. At the 95% confidence level, 95% of the confidence intervals calculated from random samples of the population contain the true population parameter.\nWe also have the option of using hypothesis testing. First, we define a null hypothesis (say the true population mean is 0), then we determine a significance level (1 - confidence level), which is the probability of rejecting the null hypothesis when it is true. Our result is statistically significant if the value for the null hypothesis is outside the confidence interval. More info.", + "crumbs": [ + "Home", + "Introduction to Data Analysis" + ] + }, + { + "objectID": "notebooks/hypothesis_testing.html", + "href": "notebooks/hypothesis_testing.html", + "title": "Hypothesis Testing", + "section": "", + "text": "Hypothesis Testing\nA hypothesis is a claim or statement about a parameter1. There are two types of statistical hypotheses: - Null Hypothesis - Alternative Hypothesis\nA null hypothesis is a statement that claims that the parameter is equal to some claimed value. - H0 is the symbol used to denote this. It can be pronounced as “H null”, “H zero” or “H nought” - It always contains one of these operators: \\(\\ge\\), \\(\\le\\), =. - This value is the one to always assume is true.\nAn alternative hypothesis is a statement that claims that the parameter is a different value than the null. - HA or H1 is the symbol used to denote this. It’s always called “alternative hypothesis.” - It always contains one of these operators: \\(\\gt\\), \\(\\lt\\), \\(\\neq\\).\n\nSteps to Solving a Hypothesis Test Problem\n\nWrite and label everything.\nWrite hypotheses:\n\nH0: (operator with equal sign)\nHA: (operator without equal sign)\n\nDraw graph (bell-curved)\n\nThe graph will either be right, left or two tailed.\n\nCarry out the necessary calculations to arrive to a solution.\n\nThis can involve solving a t-statisic or z-test.\n\nWrite a sentence summarizing the findings.\n\nUsually follows this format: “There is/is not sufficient evidence to support/reject the claim that…”\n\n\n\n\nRejection Explained\nEvery hypothesis test is rejected or failed to reject. This is because we either have enough data to be able to say the hypothesis is correct, or we don’t have enough data to prove otherwise. To determine this, we compare the significance level to the p-value .\nThe significance level is denoted by \\(\\alpha\\) which measures how strong the evidence must be in order to determine the evidence to be statistically significant.\nP-value is defined by Investopedia as “a statistical measurement used to validate a hypothesis against observed data.” We’re not going to go in-depth here regarding how the p-value is calculated, but just enough to scratch the surface. This value describes the likelihood of the data occurring randomly. P-values range from 0 to 1 and a smaller p-value denotes a smaller probability that the results occurred randomly.\n\nIf p-value \\(\\leq\\) \\(\\alpha\\), then reject H0.\nIf p-value \\(\\gt\\) \\(\\alpha\\), then fail to reject H0.\n\n\n\nDetermining the Tail of the Curve\nThe trick to remembering where the tail of the curve is by looking at the alternative hypothesis. - If the sign in HA is - \\(\\neq\\): two-tailed - \\(\\lt\\): left-taied - \\(\\gt\\): right-tailed\n\n\nErrors\nSometimes, error occurs with hypothesis testing and there are two types of it: - Type I error - This is known as the false-positive. - It occurs when the null hypothesis is rejected, but it is true. - Type II error - This is known as the false-negative. - It occurs when the null hypothesis is not rejected, but it is false.\nThis table below from Scribbr can be used to determine error type, if any.\n\n1A parameter is a measure done on an entire population of data.", + "crumbs": [ + "Home", + "Hypothesis Testing" + ] + }, + { + "objectID": "notebooks/cleaning_data.html", + "href": "notebooks/cleaning_data.html", + "title": "Cleaning Data", + "section": "", + "text": "In this notebook, we will using daily temperature data from the National Centers for Environmental Information (NCEI) API. We will use the Global Historical Climatology Network - Daily (GHCND) dataset; see the documentation here.\nThis data was collected from the LaGuardia Airport station in New York City for October 2018. It contains: - the daily minimum temperature (TMIN) - the daily maximum temperature (TMAX) - the daily average temperature (TAVG)\nNote: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.\nIn addition, we will be using S&P 500 stock market data (obtained using the stock_analysis package and data for bitcoin for 2017 through 2018. For the first edition, the bitcoin data was collected from CoinMarketCap using the stock_analysis package; however, changes in the website led to the necessity of changing the data source to Yahoo! Finance. The bitcoin data that was collected before the CoinMarketCap website change should be equivalent to the historical data that can be viewed on this page.\n\n\n\nWe need to import pandas and read in the temperature data to get started:\n\nimport pandas as pd\n\ndf = pd.read_csv('../data/nyc_temperatures.csv')\ndf.head()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nattributes\nvalue\n\n\n\n\n0\n2018-10-01T00:00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n\n\n1\n2018-10-01T00:00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n\n\n2\n2018-10-01T00:00:00\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n\n\n3\n2018-10-02T00:00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n\n\n4\n2018-10-02T00:00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n\n\n\n\n\n\n\n\n\n\nWe start out with the following columns:\n\ndf.columns\n\nIndex(['date', 'datatype', 'station', 'attributes', 'value'], dtype='object')\n\n\nWe want to rename the value column to indicate it contains the temperature in Celsius and the attributes column to say flags since each value in the comma-delimited string is a different flag about the data collection. For this task, we use the rename() method and pass in a dictionary mapping the column names to their new names. We pass inplace=True to change our original dataframe instead of getting a new one back:\n\ndf.rename(\n columns={\n 'value': 'temp_C',\n 'attributes': 'flags'\n }, inplace=True\n)\n\nThose columns have been successfully renamed:\n\ndf.columns\n\nIndex(['date', 'datatype', 'station', 'flags', 'temp_C'], dtype='object')\n\n\nWe can also perform string operations on the column names with rename():\n\ndf.rename(str.upper, axis='columns').columns\n\nIndex(['DATE', 'DATATYPE', 'STATION', 'FLAGS', 'TEMP_C'], dtype='object')\n\n\n\n\n\nThe date column is not currently being stored as a datetime:\n\ndf.dtypes\n\ndate object\ndatatype object\nstation object\nflags object\ntemp_C float64\ndtype: object\n\n\nLet’s perform the conversion with pd.to_datetime():\n\ndf.loc[:,'date'] = pd.to_datetime(df.date)\ndf.dtypes\n\ndate object\ndatatype object\nstation object\nflags object\ntemp_C float64\ndtype: object\n\n\nNow we get useful information when we use describe() on this column:\n\ndf.date.describe()\n\ncount 93\nunique 31\ntop 2018-10-01 00:00:00\nfreq 3\nName: date, dtype: object\n\n\nWe can use tz_localize() on a DatetimeIndex object to convert to a desired timezone:\n\npd.date_range(start='2018-10-25', periods=2, freq='D').tz_localize('EST')\n\nDatetimeIndex(['2018-10-25 00:00:00-05:00', '2018-10-26 00:00:00-05:00'], dtype='datetime64[ns, EST]', freq=None)\n\n\nThis also works with Series/DataFrame objects that have an index of type DatetimeIndex. Let’s read in the CSV again for this example and set the date column to be the index and stored as a datetime:\n\neastern = pd.read_csv(\n '../data/nyc_temperatures.csv', index_col='date', parse_dates=True\n).tz_localize('EST')\neastern.head()\n\n\n\n\n\n\n\n\ndatatype\nstation\nattributes\nvalue\n\n\ndate\n\n\n\n\n\n\n\n\n2018-10-01 00:00:00-05:00\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n\n\n2018-10-01 00:00:00-05:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n\n\n2018-10-01 00:00:00-05:00\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n\n\n2018-10-02 00:00:00-05:00\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n\n\n2018-10-02 00:00:00-05:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n\n\n\n\n\n\n\nWe can use tz_convert() to convert to another timezone from there. If we convert the Eastern datetimes to UTC, they will now be at 5 AM, since pandas will use the offsets to convert:\n\neastern.tz_convert('UTC').head()\n\n\n\n\n\n\n\n\ndatatype\nstation\nattributes\nvalue\n\n\ndate\n\n\n\n\n\n\n\n\n2018-10-01 05:00:00+00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n\n\n2018-10-01 05:00:00+00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n\n\n2018-10-01 05:00:00+00:00\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n\n\n2018-10-02 05:00:00+00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n\n\n2018-10-02 05:00:00+00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n\n\n\n\n\n\n\nWe can change the period of the index as well. We could change the period to be monthly to make it easier to aggregate later.\nThe reason we have to add the parameter within tz_localize() to None for this, is because we’ll get a warning from pandas that our output class PeriodArray doesn’t have time zone information and we’ll lose it.\n\neastern.tz_localize(None).to_period('M').index\n\nPeriodIndex(['2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10'],\n dtype='period[M]', name='date')\n\n\nWe now get a PeriodIndex object, which we can change back into a DatetimeIndex object with to_timestamp():\n\neastern.tz_localize(None).to_period('M').to_timestamp().index\n\nDatetimeIndex(['2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01'],\n dtype='datetime64[ns]', name='date', freq=None)\n\n\nWe can use the assign() method for working with multiple columns at once (or creating new ones). Since our date column has already been converted, we need to read in the data again:\n\ndf = pd.read_csv('../data/nyc_temperatures.csv').rename(\n columns={\n 'value': 'temp_C',\n 'attributes': 'flags'\n }\n)\n\nnew_df = df.assign(\n date=pd.to_datetime(df.date),\n temp_F=(df.temp_C * 9/5) + 32\n)\nnew_df.dtypes\n\ndate datetime64[ns]\ndatatype object\nstation object\nflags object\ntemp_C float64\ntemp_F float64\ndtype: object\n\n\nThe date column now has datetimes and the temp_F column was added:\n\nnew_df.head()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_F\n\n\n\n\n0\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n70.16\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n78.08\n\n\n2\n2018-10-01\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n64.94\n\n\n3\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n72.86\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n78.98\n\n\n\n\n\n\n\nWe can also use astype() to perform conversions. Let’s create columns of the integer portion of the temperatures in Celsius and Fahrenheit. We will use lambda functions (first introduced in Chapter 2, Working with Pandas DataFrames), so that we can use the values being created in the temp_F column to calculate the temp_F_whole column. It is very common (and useful) to use lambda functions with assign():\n\ndf = df.assign(\n date=lambda x: pd.to_datetime(x.date),\n temp_C_whole=lambda x: x.temp_C.astype('int'),\n temp_F=lambda x: (x.temp_C * 9/5) + 32,\n temp_F_whole=lambda x: x.temp_F.astype('int')\n)\n\ndf.head()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n21\n70.16\n70\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n2\n2018-10-01\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n18\n64.94\n64\n\n\n3\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n22\n72.86\n72\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nCreating categories:\n\ndf_with_categories = df.assign(\n station=df.station.astype('category'),\n datatype=df.datatype.astype('category')\n)\ndf_with_categories.dtypes\n\ndate datetime64[ns]\ndatatype category\nstation category\nflags object\ntemp_C float64\ntemp_C_whole int32\ntemp_F float64\ntemp_F_whole int32\ndtype: object\n\n\n\ndf_with_categories.describe(include='category')\n\n\n\n\n\n\n\n\ndatatype\nstation\n\n\n\n\ncount\n93\n93\n\n\nunique\n3\n1\n\n\ntop\nTAVG\nGHCND:USW00014732\n\n\nfreq\n31\n93\n\n\n\n\n\n\n\nOur categories have no order, but this is something that pandas supports:\n\npd.Categorical(\n ['med', 'med', 'low', 'high'], \n categories=['low', 'med', 'high'],\n ordered=True\n)\n\n['med', 'med', 'low', 'high']\nCategories (3, object): ['low' < 'med' < 'high']\n\n\n\n\n\nSay we want to find the days that reached the hottest temperatures in the weather data; we can sort our values by the temp_C column with the largest on top to find this:\n\ndf[df.datatype == 'TMAX'].sort_values(by='temp_C', ascending=False).head(10)\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n19\n2018-10-07\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n28\n2018-10-10\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n31\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n10\n2018-10-04\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n25\n2018-10-09\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n7\n2018-10-03\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.0\n25\n77.00\n77\n\n\n13\n2018-10-05\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n22\n2018-10-08\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n\n\n\n\n\nHowever, this isn’t perfect because we have some ties, and they aren’t sorted consistently. In the first tie between the 7th and the 10th, the earlier date comes first, but the opposite is true with the tie between the 4th and the 2nd. We can use other columns to break ties and specify how to sort each with ascending. Let’s break ties with the date column and show earlier dates before later ones:\n\ndf[df.datatype == 'TMAX'].sort_values(by=['temp_C', 'date'], ascending=[False, True]).head(10)\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n19\n2018-10-07\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n28\n2018-10-10\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n31\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n10\n2018-10-04\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n25\n2018-10-09\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n7\n2018-10-03\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.0\n25\n77.00\n77\n\n\n13\n2018-10-05\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n22\n2018-10-08\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n\n\n\n\n\nNotice that the index was jumbled in the past 2 results. Here, our index only stores the row number in the original data, but we may not need to keep track of that information. In this case, we can pass in ignore_index=True to get a new index after sorting:\n\ndf[df.datatype == 'TMAX'].sort_values(by=['temp_C', 'date'], ascending=[False, True], ignore_index=True).head(10)\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n2018-10-07\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n1\n2018-10-10\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n2\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n3\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n4\n2018-10-04\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n5\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n6\n2018-10-09\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n7\n2018-10-03\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.0\n25\n77.00\n77\n\n\n8\n2018-10-05\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n9\n2018-10-08\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n\n\n\n\n\nWhen just looking for the n-largest values, rather than wanting to sort all the data, we can use nlargest():\n\ndf[df.datatype == 'TAVG'].nlargest(n=10, columns='temp_C')\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n27\n2018-10-10\nTAVG\nGHCND:USW00014732\nH,,S,\n23.8\n23\n74.84\n74\n\n\n30\n2018-10-11\nTAVG\nGHCND:USW00014732\nH,,S,\n23.4\n23\n74.12\n74\n\n\n18\n2018-10-07\nTAVG\nGHCND:USW00014732\nH,,S,\n22.8\n22\n73.04\n73\n\n\n3\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n22\n72.86\n72\n\n\n6\n2018-10-03\nTAVG\nGHCND:USW00014732\nH,,S,\n21.8\n21\n71.24\n71\n\n\n24\n2018-10-09\nTAVG\nGHCND:USW00014732\nH,,S,\n21.8\n21\n71.24\n71\n\n\n9\n2018-10-04\nTAVG\nGHCND:USW00014732\nH,,S,\n21.3\n21\n70.34\n70\n\n\n0\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n21\n70.16\n70\n\n\n21\n2018-10-08\nTAVG\nGHCND:USW00014732\nH,,S,\n20.9\n20\n69.62\n69\n\n\n12\n2018-10-05\nTAVG\nGHCND:USW00014732\nH,,S,\n20.3\n20\n68.54\n68\n\n\n\n\n\n\n\nWe use nsmallest() for the n-smallest values.\n\ndf.nsmallest(n=5, columns=['temp_C', 'date'])\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n65\n2018-10-22\nTMIN\nGHCND:USW00014732\n,,W,2400\n5.6\n5\n42.08\n42\n\n\n77\n2018-10-26\nTMIN\nGHCND:USW00014732\n,,W,2400\n5.6\n5\n42.08\n42\n\n\n62\n2018-10-21\nTMIN\nGHCND:USW00014732\n,,W,2400\n6.1\n6\n42.98\n42\n\n\n74\n2018-10-25\nTMIN\nGHCND:USW00014732\n,,W,2400\n6.1\n6\n42.98\n42\n\n\n53\n2018-10-18\nTMIN\nGHCND:USW00014732\n,,W,2400\n6.7\n6\n44.06\n44\n\n\n\n\n\n\n\nThe sample() method will give us rows (or columns with axis=1) at random. We can provide a seed (random_state) to make this reproducible. The index after we do this is jumbled:\n\ndf.sample(5, random_state=0).index\n\nIndex([2, 30, 55, 16, 13], dtype='int64')\n\n\nWe can use sort_index() to order it again:\n\ndf.sample(5, random_state=0).sort_index().index\n\nIndex([2, 13, 16, 30, 55], dtype='int64')\n\n\nThe sort_index() method can also sort columns alphabetically:\n\ndf.sort_index(axis=1).head()\n\n\n\n\n\n\n\n\ndatatype\ndate\nflags\nstation\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\nTAVG\n2018-10-01\nH,,S,\nGHCND:USW00014732\n21.2\n21\n70.16\n70\n\n\n1\nTMAX\n2018-10-01\n,,W,2400\nGHCND:USW00014732\n25.6\n25\n78.08\n78\n\n\n2\nTMIN\n2018-10-01\n,,W,2400\nGHCND:USW00014732\n18.3\n18\n64.94\n64\n\n\n3\nTAVG\n2018-10-02\nH,,S,\nGHCND:USW00014732\n22.7\n22\n72.86\n72\n\n\n4\nTMAX\n2018-10-02\n,,W,2400\nGHCND:USW00014732\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nThis can make selection with loc easier for many columns:\n\ndf.sort_index(axis=1).head().loc[:,'temp_C':'temp_F_whole']\n\n\n\n\n\n\n\n\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n21.2\n21\n70.16\n70\n\n\n1\n25.6\n25\n78.08\n78\n\n\n2\n18.3\n18\n64.94\n64\n\n\n3\n22.7\n22\n72.86\n72\n\n\n4\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nWe must sort the index to compare two dataframes. If the index is different, but the data is the same, they will be marked not-equal:\n\ndf.equals(df.sort_values(by='temp_C'))\n\nFalse\n\n\nSorting the index solves this issue:\n\ndf.equals(df.sort_values(by='temp_C').sort_index())\n\nTrue\n\n\nLet’s set the date column as our index:\n\ndf.set_index('date', inplace=True)\ndf.head()\n\n\n\n\n\n\n\n\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n21\n70.16\n70\n\n\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n2018-10-01\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n18\n64.94\n64\n\n\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n22\n72.86\n72\n\n\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nNow that we have an index of type DatetimeIndex, we can do datetime slicing and indexing. As long as we provide a date format that pandas understands, we can grab the data. To select all of 2018, we simply use df.loc['2018'], for the fourth quarter of 2018 we can use df.loc['2018-Q4'], grabbing October is as simple as using df.loc['2018-10']; these can also be combined to build ranges. Let’s grab October 11, 2018 through October 12, 2018 (inclusive of both endpoints)—note that using loc[] is optional for ranges:\n\ndf['2018-10-11':'2018-10-12']\n\n\n\n\n\n\n\n\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n2018-10-11\nTAVG\nGHCND:USW00014732\nH,,S,\n23.4\n23\n74.12\n74\n\n\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n2018-10-11\nTMIN\nGHCND:USW00014732\n,,W,2400\n21.7\n21\n71.06\n71\n\n\n2018-10-12\nTAVG\nGHCND:USW00014732\nH,,S,\n18.3\n18\n64.94\n64\n\n\n2018-10-12\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.2\n22\n71.96\n71\n\n\n2018-10-12\nTMIN\nGHCND:USW00014732\n,,W,2400\n12.2\n12\n53.96\n53\n\n\n\n\n\n\n\nWe can also use reset_index() to get a fresh index and move our current index into a column for safe keeping. This is especially useful if we had data, such as the date, in the index that we don’t want to lose:\n\ndf['2018-10-11':'2018-10-12'].reset_index()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n2018-10-11\nTAVG\nGHCND:USW00014732\nH,,S,\n23.4\n23\n74.12\n74\n\n\n1\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n2\n2018-10-11\nTMIN\nGHCND:USW00014732\n,,W,2400\n21.7\n21\n71.06\n71\n\n\n3\n2018-10-12\nTAVG\nGHCND:USW00014732\nH,,S,\n18.3\n18\n64.94\n64\n\n\n4\n2018-10-12\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.2\n22\n71.96\n71\n\n\n5\n2018-10-12\nTMIN\nGHCND:USW00014732\n,,W,2400\n12.2\n12\n53.96\n53\n\n\n\n\n\n\n\nReindexing allows us to conform our axis to contain a given set of labels. Let’s turn to the S&P 500 stock data in the sp500.csv file to see an example of this. Notice we only have data for trading days (weekdays, excluding holidays):\n\nsp = pd.read_csv(\n '../data/sp500.csv', index_col='date', parse_dates=True\n).drop(columns=['adj_close'])\n\nsp.head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3770530000\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3764890000\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3761820000\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3339890000\nFriday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3217610000\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3638790000\nTuesday\n\n\n2017-01-11\n2275.320068\n2260.830078\n2268.600098\n2275.320068\n3620410000\nWednesday\n\n\n2017-01-12\n2271.780029\n2254.250000\n2271.139893\n2270.439941\n3462130000\nThursday\n\n\n2017-01-13\n2278.679932\n2271.510010\n2272.739990\n2274.639893\n3081270000\nFriday\n\n\n2017-01-17\n2272.080078\n2262.810059\n2269.139893\n2267.889893\n3584990000\nTuesday\n\n\n\n\n\n\n\nIf we want to look at the value of a portfolio (group of assets) that trade on different days, we need to handle the mismatch in the index. Bitcoin, for example, trades daily. If we sum up all the data we have for each day (aggregations will be covered in chapter 4, so don’t fixate on this part), we get the following:\n\nbitcoin = pd.read_csv(\n '../data/bitcoin.csv', index_col='date', parse_dates=True\n).drop(columns=['market_cap'])\n\n# every day's closing price = S&P 500 close + Bitcoin close (same for other metrics)\nportfolio = pd.concat([sp, bitcoin], sort=False).groupby(level='date').sum()\n\nportfolio.head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\n1003.080000\n958.700000\n963.660000\n998.330000\n147775008\nSunday\n\n\n2017-01-02\n1031.390000\n996.700000\n998.620000\n1021.750000\n222184992\nMonday\n\n\n2017-01-03\n3307.959883\n3266.729883\n3273.170068\n3301.670078\n3955698000\nTuesday\n\n\n2017-01-04\n3432.240068\n3306.000098\n3306.000098\n3425.480000\n4109835984\nWednesday\n\n\n2017-01-05\n3462.600000\n3170.869951\n3424.909932\n3282.380000\n4272019008\nThursday\n\n\n2017-01-06\n3328.910098\n3148.000059\n3285.379893\n3179.179980\n3691766000\nFriday\n\n\n2017-01-07\n908.590000\n823.560000\n903.490000\n908.590000\n279550016\nSaturday\n\n\n2017-01-08\n942.720000\n887.250000\n908.170000\n911.200000\n158715008\nSunday\n\n\n2017-01-09\n3189.179990\n3148.709902\n3186.830088\n3171.729902\n3359486992\nMonday\n\n\n2017-01-10\n3194.140020\n3166.330020\n3172.159971\n3176.579902\n3754598000\nTuesday\n\n\n\n\n\n\n\nIt may not be immediately obvious what is wrong with the previous data, but with a visualization we can easily see the cyclical pattern of drops on the days the stock market is closed. (Don’t worry about the plotting code too much, we will cover it in depth in chapters 5 and 6).\nWe will need to import matplotlib now:\n\nimport matplotlib.pyplot as plt # we use this module for plotting\nfrom matplotlib.ticker import StrMethodFormatter # for formatting the axis\n\nNow we can see why we need to reindex:\n\n# plot the closing price from Q4 2017 through Q2 2018\nax = portfolio['2017-Q4':'2018-Q2'].plot(\n y='close', figsize=(15, 5), legend=False,\n title='Bitcoin + S&P 500 value without accounting for different indices'\n)\n\n# formatting\nax.set_ylabel('price')\nax.yaxis.set_major_formatter(StrMethodFormatter('${x:,.0f}'))\nfor spine in ['top', 'right']:\n ax.spines[spine].set_visible(False)\n\n# show the plot\nplt.show()\n\n\n\n\n\n\n\n\nWe need to align the index of the S&P 500 to match bitcoin in order to fix this. We will use the reindex() method, but by default we get NaN for the values that we don’t have data for:\n\nsp.reindex(bitcoin.index).head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\nNaN\nNaN\nNaN\nNaN\nNaN\nSunday\n\n\n2017-01-02\nNaN\nNaN\nNaN\nNaN\nNaN\nMonday\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3.770530e+09\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3.764890e+09\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3.761820e+09\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nFriday\n\n\n2017-01-07\nNaN\nNaN\nNaN\nNaN\nNaN\nSaturday\n\n\n2017-01-08\nNaN\nNaN\nNaN\nNaN\nNaN\nSunday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3.217610e+09\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3.638790e+09\nTuesday\n\n\n\n\n\n\n\nSo now we have rows for every day of the year, but all the weekends and holidays have NaN values. To address this, we can specify how to handle missing values with the method argument. In this case, we want to forward-fill, which will put the weekend and holiday values as the value they had for the Friday (or end of trading week) before:\n\nsp.reindex(bitcoin.index, method='ffill').head(10)\\\n .assign(day_of_week=lambda x: x.index.day_name())\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\nNaN\nNaN\nNaN\nNaN\nNaN\nSunday\n\n\n2017-01-02\nNaN\nNaN\nNaN\nNaN\nNaN\nMonday\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3.770530e+09\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3.764890e+09\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3.761820e+09\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nFriday\n\n\n2017-01-07\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nSaturday\n\n\n2017-01-08\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nSunday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3.217610e+09\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3.638790e+09\nTuesday\n\n\n\n\n\n\n\nTo isolate the changes happening with the forward-filling, we can use the compare() method. It shows us the values that differ across identically-labeled dataframes (same names and same columns). Here, we can see that only weekends and holidays (Monday, January 16, 2017 was MLK day) have values forward-filled. Notice that consecutive days have the same values.\n\nsp.reindex(bitcoin.index)\\\n .compare(sp.reindex(bitcoin.index, method='ffill'))\\\n .head(10).assign(day_of_week=lambda x: x.index.day_name())\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\n\nself\nother\nself\nother\nself\nother\nself\nother\nself\nother\n\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n2017-01-07\nNaN\n2282.100098\nNaN\n2264.060059\nNaN\n2271.139893\nNaN\n2276.979980\nNaN\n3.339890e+09\nSaturday\n\n\n2017-01-08\nNaN\n2282.100098\nNaN\n2264.060059\nNaN\n2271.139893\nNaN\n2276.979980\nNaN\n3.339890e+09\nSunday\n\n\n2017-01-14\nNaN\n2278.679932\nNaN\n2271.510010\nNaN\n2272.739990\nNaN\n2274.639893\nNaN\n3.081270e+09\nSaturday\n\n\n2017-01-15\nNaN\n2278.679932\nNaN\n2271.510010\nNaN\n2272.739990\nNaN\n2274.639893\nNaN\n3.081270e+09\nSunday\n\n\n2017-01-16\nNaN\n2278.679932\nNaN\n2271.510010\nNaN\n2272.739990\nNaN\n2274.639893\nNaN\n3.081270e+09\nMonday\n\n\n2017-01-21\nNaN\n2276.959961\nNaN\n2265.010010\nNaN\n2269.959961\nNaN\n2271.310059\nNaN\n3.524970e+09\nSaturday\n\n\n2017-01-22\nNaN\n2276.959961\nNaN\n2265.010010\nNaN\n2269.959961\nNaN\n2271.310059\nNaN\n3.524970e+09\nSunday\n\n\n2017-01-28\nNaN\n2299.020020\nNaN\n2291.620117\nNaN\n2299.020020\nNaN\n2294.689941\nNaN\n3.135890e+09\nSaturday\n\n\n2017-01-29\nNaN\n2299.020020\nNaN\n2291.620117\nNaN\n2299.020020\nNaN\n2294.689941\nNaN\n3.135890e+09\nSunday\n\n\n2017-02-04\nNaN\n2298.310059\nNaN\n2287.879883\nNaN\n2288.540039\nNaN\n2297.419922\nNaN\n3.597970e+09\nSaturday\n\n\n\n\n\n\n\nThis isn’t perfect though. We probably want 0 for the volume traded and to put the closing price for the open, high, low, and close on the days the market is closed:\nThe reason why we’re using np.where(boolean condition, value if True, value if False) within lambda functions in the example below, is that vectorized operations allow us to be faster and more efficient than utilizing for loops to perform calculations on arrays all at once.\n\nimport numpy as np\n\nsp_reindexed = sp.reindex(bitcoin.index).assign(\n volume=lambda x: x.volume.fillna(0), # put 0 when market is closed\n close=lambda x: x.close.fillna(method='ffill'), # carry this forward\n # take the closing price if these aren't available\n open=lambda x: np.where(x.open.isnull(), x.close, x.open),\n high=lambda x: np.where(x.high.isnull(), x.close, x.high),\n low=lambda x: np.where(x.low.isnull(), x.close, x.low)\n)\nsp_reindexed.head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\nNaN\nNaN\nNaN\nNaN\n0.000000e+00\nSunday\n\n\n2017-01-02\nNaN\nNaN\nNaN\nNaN\n0.000000e+00\nMonday\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3.770530e+09\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3.764890e+09\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3.761820e+09\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nFriday\n\n\n2017-01-07\n2276.979980\n2276.979980\n2276.979980\n2276.979980\n0.000000e+00\nSaturday\n\n\n2017-01-08\n2276.979980\n2276.979980\n2276.979980\n2276.979980\n0.000000e+00\nSunday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3.217610e+09\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3.638790e+09\nTuesday\n\n\n\n\n\n\n\nIf we create a visualization comparing the reindexed data to the first attempt, we see how reindexing helped maintain the asset value when the market was closed:\n\n# every day's closing price = S&P 500 close adjusted for market closure + Bitcoin close (same for other metrics)\nfixed_portfolio = sp_reindexed + bitcoin\n\n# plot the reindexed portfolio's closing price from Q4 2017 through Q2 2018\nax = fixed_portfolio['2017-Q4':'2018-Q2'].plot(\n y='close', label='reindexed portfolio of S&P 500 + Bitcoin', figsize=(15, 5), linewidth=2, \n title='Reindexed portfolio vs. portfolio with mismatched indices'\n)\n\n# add line for original portfolio for comparison\nportfolio['2017-Q4':'2018-Q2'].plot(\n y='close', ax=ax, linestyle='--', label='portfolio of S&P 500 + Bitcoin w/o reindexing'\n)\n\n# formatting\nax.set_ylabel('price')\nax.yaxis.set_major_formatter(StrMethodFormatter('${x:,.0f}'))\nfor spine in ['top', 'right']:\n ax.spines[spine].set_visible(False)\n\n# show the plot\nplt.show()", + "crumbs": [ + "Home", + "Cleaning Data" + ] + }, + { + "objectID": "notebooks/cleaning_data.html#about-the-data", + "href": "notebooks/cleaning_data.html#about-the-data", + "title": "Cleaning Data", + "section": "", + "text": "In this notebook, we will using daily temperature data from the National Centers for Environmental Information (NCEI) API. We will use the Global Historical Climatology Network - Daily (GHCND) dataset; see the documentation here.\nThis data was collected from the LaGuardia Airport station in New York City for October 2018. It contains: - the daily minimum temperature (TMIN) - the daily maximum temperature (TMAX) - the daily average temperature (TAVG)\nNote: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.\nIn addition, we will be using S&P 500 stock market data (obtained using the stock_analysis package and data for bitcoin for 2017 through 2018. For the first edition, the bitcoin data was collected from CoinMarketCap using the stock_analysis package; however, changes in the website led to the necessity of changing the data source to Yahoo! Finance. The bitcoin data that was collected before the CoinMarketCap website change should be equivalent to the historical data that can be viewed on this page.", + "crumbs": [ + "Home", + "Cleaning Data" + ] + }, + { + "objectID": "notebooks/cleaning_data.html#setup", + "href": "notebooks/cleaning_data.html#setup", + "title": "Cleaning Data", + "section": "", + "text": "We need to import pandas and read in the temperature data to get started:\n\nimport pandas as pd\n\ndf = pd.read_csv('../data/nyc_temperatures.csv')\ndf.head()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nattributes\nvalue\n\n\n\n\n0\n2018-10-01T00:00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n\n\n1\n2018-10-01T00:00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n\n\n2\n2018-10-01T00:00:00\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n\n\n3\n2018-10-02T00:00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n\n\n4\n2018-10-02T00:00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1", + "crumbs": [ + "Home", + "Cleaning Data" + ] + }, + { + "objectID": "notebooks/cleaning_data.html#renaming-columns", + "href": "notebooks/cleaning_data.html#renaming-columns", + "title": "Cleaning Data", + "section": "", + "text": "We start out with the following columns:\n\ndf.columns\n\nIndex(['date', 'datatype', 'station', 'attributes', 'value'], dtype='object')\n\n\nWe want to rename the value column to indicate it contains the temperature in Celsius and the attributes column to say flags since each value in the comma-delimited string is a different flag about the data collection. For this task, we use the rename() method and pass in a dictionary mapping the column names to their new names. We pass inplace=True to change our original dataframe instead of getting a new one back:\n\ndf.rename(\n columns={\n 'value': 'temp_C',\n 'attributes': 'flags'\n }, inplace=True\n)\n\nThose columns have been successfully renamed:\n\ndf.columns\n\nIndex(['date', 'datatype', 'station', 'flags', 'temp_C'], dtype='object')\n\n\nWe can also perform string operations on the column names with rename():\n\ndf.rename(str.upper, axis='columns').columns\n\nIndex(['DATE', 'DATATYPE', 'STATION', 'FLAGS', 'TEMP_C'], dtype='object')", + "crumbs": [ + "Home", + "Cleaning Data" + ] + }, + { + "objectID": "notebooks/cleaning_data.html#type-conversion", + "href": "notebooks/cleaning_data.html#type-conversion", + "title": "Cleaning Data", + "section": "", + "text": "The date column is not currently being stored as a datetime:\n\ndf.dtypes\n\ndate object\ndatatype object\nstation object\nflags object\ntemp_C float64\ndtype: object\n\n\nLet’s perform the conversion with pd.to_datetime():\n\ndf.loc[:,'date'] = pd.to_datetime(df.date)\ndf.dtypes\n\ndate object\ndatatype object\nstation object\nflags object\ntemp_C float64\ndtype: object\n\n\nNow we get useful information when we use describe() on this column:\n\ndf.date.describe()\n\ncount 93\nunique 31\ntop 2018-10-01 00:00:00\nfreq 3\nName: date, dtype: object\n\n\nWe can use tz_localize() on a DatetimeIndex object to convert to a desired timezone:\n\npd.date_range(start='2018-10-25', periods=2, freq='D').tz_localize('EST')\n\nDatetimeIndex(['2018-10-25 00:00:00-05:00', '2018-10-26 00:00:00-05:00'], dtype='datetime64[ns, EST]', freq=None)\n\n\nThis also works with Series/DataFrame objects that have an index of type DatetimeIndex. Let’s read in the CSV again for this example and set the date column to be the index and stored as a datetime:\n\neastern = pd.read_csv(\n '../data/nyc_temperatures.csv', index_col='date', parse_dates=True\n).tz_localize('EST')\neastern.head()\n\n\n\n\n\n\n\n\ndatatype\nstation\nattributes\nvalue\n\n\ndate\n\n\n\n\n\n\n\n\n2018-10-01 00:00:00-05:00\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n\n\n2018-10-01 00:00:00-05:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n\n\n2018-10-01 00:00:00-05:00\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n\n\n2018-10-02 00:00:00-05:00\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n\n\n2018-10-02 00:00:00-05:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n\n\n\n\n\n\n\nWe can use tz_convert() to convert to another timezone from there. If we convert the Eastern datetimes to UTC, they will now be at 5 AM, since pandas will use the offsets to convert:\n\neastern.tz_convert('UTC').head()\n\n\n\n\n\n\n\n\ndatatype\nstation\nattributes\nvalue\n\n\ndate\n\n\n\n\n\n\n\n\n2018-10-01 05:00:00+00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n\n\n2018-10-01 05:00:00+00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n\n\n2018-10-01 05:00:00+00:00\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n\n\n2018-10-02 05:00:00+00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n\n\n2018-10-02 05:00:00+00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n\n\n\n\n\n\n\nWe can change the period of the index as well. We could change the period to be monthly to make it easier to aggregate later.\nThe reason we have to add the parameter within tz_localize() to None for this, is because we’ll get a warning from pandas that our output class PeriodArray doesn’t have time zone information and we’ll lose it.\n\neastern.tz_localize(None).to_period('M').index\n\nPeriodIndex(['2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10'],\n dtype='period[M]', name='date')\n\n\nWe now get a PeriodIndex object, which we can change back into a DatetimeIndex object with to_timestamp():\n\neastern.tz_localize(None).to_period('M').to_timestamp().index\n\nDatetimeIndex(['2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01'],\n dtype='datetime64[ns]', name='date', freq=None)\n\n\nWe can use the assign() method for working with multiple columns at once (or creating new ones). Since our date column has already been converted, we need to read in the data again:\n\ndf = pd.read_csv('../data/nyc_temperatures.csv').rename(\n columns={\n 'value': 'temp_C',\n 'attributes': 'flags'\n }\n)\n\nnew_df = df.assign(\n date=pd.to_datetime(df.date),\n temp_F=(df.temp_C * 9/5) + 32\n)\nnew_df.dtypes\n\ndate datetime64[ns]\ndatatype object\nstation object\nflags object\ntemp_C float64\ntemp_F float64\ndtype: object\n\n\nThe date column now has datetimes and the temp_F column was added:\n\nnew_df.head()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_F\n\n\n\n\n0\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n70.16\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n78.08\n\n\n2\n2018-10-01\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n64.94\n\n\n3\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n72.86\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n78.98\n\n\n\n\n\n\n\nWe can also use astype() to perform conversions. Let’s create columns of the integer portion of the temperatures in Celsius and Fahrenheit. We will use lambda functions (first introduced in Chapter 2, Working with Pandas DataFrames), so that we can use the values being created in the temp_F column to calculate the temp_F_whole column. It is very common (and useful) to use lambda functions with assign():\n\ndf = df.assign(\n date=lambda x: pd.to_datetime(x.date),\n temp_C_whole=lambda x: x.temp_C.astype('int'),\n temp_F=lambda x: (x.temp_C * 9/5) + 32,\n temp_F_whole=lambda x: x.temp_F.astype('int')\n)\n\ndf.head()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n21\n70.16\n70\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n2\n2018-10-01\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n18\n64.94\n64\n\n\n3\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n22\n72.86\n72\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nCreating categories:\n\ndf_with_categories = df.assign(\n station=df.station.astype('category'),\n datatype=df.datatype.astype('category')\n)\ndf_with_categories.dtypes\n\ndate datetime64[ns]\ndatatype category\nstation category\nflags object\ntemp_C float64\ntemp_C_whole int32\ntemp_F float64\ntemp_F_whole int32\ndtype: object\n\n\n\ndf_with_categories.describe(include='category')\n\n\n\n\n\n\n\n\ndatatype\nstation\n\n\n\n\ncount\n93\n93\n\n\nunique\n3\n1\n\n\ntop\nTAVG\nGHCND:USW00014732\n\n\nfreq\n31\n93\n\n\n\n\n\n\n\nOur categories have no order, but this is something that pandas supports:\n\npd.Categorical(\n ['med', 'med', 'low', 'high'], \n categories=['low', 'med', 'high'],\n ordered=True\n)\n\n['med', 'med', 'low', 'high']\nCategories (3, object): ['low' < 'med' < 'high']", + "crumbs": [ + "Home", + "Cleaning Data" + ] + }, + { + "objectID": "notebooks/cleaning_data.html#reordering-reindexing-and-sorting", + "href": "notebooks/cleaning_data.html#reordering-reindexing-and-sorting", + "title": "Cleaning Data", + "section": "", + "text": "Say we want to find the days that reached the hottest temperatures in the weather data; we can sort our values by the temp_C column with the largest on top to find this:\n\ndf[df.datatype == 'TMAX'].sort_values(by='temp_C', ascending=False).head(10)\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n19\n2018-10-07\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n28\n2018-10-10\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n31\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n10\n2018-10-04\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n25\n2018-10-09\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n7\n2018-10-03\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.0\n25\n77.00\n77\n\n\n13\n2018-10-05\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n22\n2018-10-08\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n\n\n\n\n\nHowever, this isn’t perfect because we have some ties, and they aren’t sorted consistently. In the first tie between the 7th and the 10th, the earlier date comes first, but the opposite is true with the tie between the 4th and the 2nd. We can use other columns to break ties and specify how to sort each with ascending. Let’s break ties with the date column and show earlier dates before later ones:\n\ndf[df.datatype == 'TMAX'].sort_values(by=['temp_C', 'date'], ascending=[False, True]).head(10)\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n19\n2018-10-07\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n28\n2018-10-10\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n31\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n10\n2018-10-04\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n25\n2018-10-09\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n7\n2018-10-03\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.0\n25\n77.00\n77\n\n\n13\n2018-10-05\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n22\n2018-10-08\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n\n\n\n\n\nNotice that the index was jumbled in the past 2 results. Here, our index only stores the row number in the original data, but we may not need to keep track of that information. In this case, we can pass in ignore_index=True to get a new index after sorting:\n\ndf[df.datatype == 'TMAX'].sort_values(by=['temp_C', 'date'], ascending=[False, True], ignore_index=True).head(10)\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n2018-10-07\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n1\n2018-10-10\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n2\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n3\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n4\n2018-10-04\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n5\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n6\n2018-10-09\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n7\n2018-10-03\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.0\n25\n77.00\n77\n\n\n8\n2018-10-05\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n9\n2018-10-08\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n\n\n\n\n\nWhen just looking for the n-largest values, rather than wanting to sort all the data, we can use nlargest():\n\ndf[df.datatype == 'TAVG'].nlargest(n=10, columns='temp_C')\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n27\n2018-10-10\nTAVG\nGHCND:USW00014732\nH,,S,\n23.8\n23\n74.84\n74\n\n\n30\n2018-10-11\nTAVG\nGHCND:USW00014732\nH,,S,\n23.4\n23\n74.12\n74\n\n\n18\n2018-10-07\nTAVG\nGHCND:USW00014732\nH,,S,\n22.8\n22\n73.04\n73\n\n\n3\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n22\n72.86\n72\n\n\n6\n2018-10-03\nTAVG\nGHCND:USW00014732\nH,,S,\n21.8\n21\n71.24\n71\n\n\n24\n2018-10-09\nTAVG\nGHCND:USW00014732\nH,,S,\n21.8\n21\n71.24\n71\n\n\n9\n2018-10-04\nTAVG\nGHCND:USW00014732\nH,,S,\n21.3\n21\n70.34\n70\n\n\n0\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n21\n70.16\n70\n\n\n21\n2018-10-08\nTAVG\nGHCND:USW00014732\nH,,S,\n20.9\n20\n69.62\n69\n\n\n12\n2018-10-05\nTAVG\nGHCND:USW00014732\nH,,S,\n20.3\n20\n68.54\n68\n\n\n\n\n\n\n\nWe use nsmallest() for the n-smallest values.\n\ndf.nsmallest(n=5, columns=['temp_C', 'date'])\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n65\n2018-10-22\nTMIN\nGHCND:USW00014732\n,,W,2400\n5.6\n5\n42.08\n42\n\n\n77\n2018-10-26\nTMIN\nGHCND:USW00014732\n,,W,2400\n5.6\n5\n42.08\n42\n\n\n62\n2018-10-21\nTMIN\nGHCND:USW00014732\n,,W,2400\n6.1\n6\n42.98\n42\n\n\n74\n2018-10-25\nTMIN\nGHCND:USW00014732\n,,W,2400\n6.1\n6\n42.98\n42\n\n\n53\n2018-10-18\nTMIN\nGHCND:USW00014732\n,,W,2400\n6.7\n6\n44.06\n44\n\n\n\n\n\n\n\nThe sample() method will give us rows (or columns with axis=1) at random. We can provide a seed (random_state) to make this reproducible. The index after we do this is jumbled:\n\ndf.sample(5, random_state=0).index\n\nIndex([2, 30, 55, 16, 13], dtype='int64')\n\n\nWe can use sort_index() to order it again:\n\ndf.sample(5, random_state=0).sort_index().index\n\nIndex([2, 13, 16, 30, 55], dtype='int64')\n\n\nThe sort_index() method can also sort columns alphabetically:\n\ndf.sort_index(axis=1).head()\n\n\n\n\n\n\n\n\ndatatype\ndate\nflags\nstation\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\nTAVG\n2018-10-01\nH,,S,\nGHCND:USW00014732\n21.2\n21\n70.16\n70\n\n\n1\nTMAX\n2018-10-01\n,,W,2400\nGHCND:USW00014732\n25.6\n25\n78.08\n78\n\n\n2\nTMIN\n2018-10-01\n,,W,2400\nGHCND:USW00014732\n18.3\n18\n64.94\n64\n\n\n3\nTAVG\n2018-10-02\nH,,S,\nGHCND:USW00014732\n22.7\n22\n72.86\n72\n\n\n4\nTMAX\n2018-10-02\n,,W,2400\nGHCND:USW00014732\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nThis can make selection with loc easier for many columns:\n\ndf.sort_index(axis=1).head().loc[:,'temp_C':'temp_F_whole']\n\n\n\n\n\n\n\n\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n21.2\n21\n70.16\n70\n\n\n1\n25.6\n25\n78.08\n78\n\n\n2\n18.3\n18\n64.94\n64\n\n\n3\n22.7\n22\n72.86\n72\n\n\n4\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nWe must sort the index to compare two dataframes. If the index is different, but the data is the same, they will be marked not-equal:\n\ndf.equals(df.sort_values(by='temp_C'))\n\nFalse\n\n\nSorting the index solves this issue:\n\ndf.equals(df.sort_values(by='temp_C').sort_index())\n\nTrue\n\n\nLet’s set the date column as our index:\n\ndf.set_index('date', inplace=True)\ndf.head()\n\n\n\n\n\n\n\n\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n21\n70.16\n70\n\n\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n2018-10-01\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n18\n64.94\n64\n\n\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n22\n72.86\n72\n\n\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nNow that we have an index of type DatetimeIndex, we can do datetime slicing and indexing. As long as we provide a date format that pandas understands, we can grab the data. To select all of 2018, we simply use df.loc['2018'], for the fourth quarter of 2018 we can use df.loc['2018-Q4'], grabbing October is as simple as using df.loc['2018-10']; these can also be combined to build ranges. Let’s grab October 11, 2018 through October 12, 2018 (inclusive of both endpoints)—note that using loc[] is optional for ranges:\n\ndf['2018-10-11':'2018-10-12']\n\n\n\n\n\n\n\n\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n2018-10-11\nTAVG\nGHCND:USW00014732\nH,,S,\n23.4\n23\n74.12\n74\n\n\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n2018-10-11\nTMIN\nGHCND:USW00014732\n,,W,2400\n21.7\n21\n71.06\n71\n\n\n2018-10-12\nTAVG\nGHCND:USW00014732\nH,,S,\n18.3\n18\n64.94\n64\n\n\n2018-10-12\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.2\n22\n71.96\n71\n\n\n2018-10-12\nTMIN\nGHCND:USW00014732\n,,W,2400\n12.2\n12\n53.96\n53\n\n\n\n\n\n\n\nWe can also use reset_index() to get a fresh index and move our current index into a column for safe keeping. This is especially useful if we had data, such as the date, in the index that we don’t want to lose:\n\ndf['2018-10-11':'2018-10-12'].reset_index()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n2018-10-11\nTAVG\nGHCND:USW00014732\nH,,S,\n23.4\n23\n74.12\n74\n\n\n1\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n2\n2018-10-11\nTMIN\nGHCND:USW00014732\n,,W,2400\n21.7\n21\n71.06\n71\n\n\n3\n2018-10-12\nTAVG\nGHCND:USW00014732\nH,,S,\n18.3\n18\n64.94\n64\n\n\n4\n2018-10-12\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.2\n22\n71.96\n71\n\n\n5\n2018-10-12\nTMIN\nGHCND:USW00014732\n,,W,2400\n12.2\n12\n53.96\n53\n\n\n\n\n\n\n\nReindexing allows us to conform our axis to contain a given set of labels. Let’s turn to the S&P 500 stock data in the sp500.csv file to see an example of this. Notice we only have data for trading days (weekdays, excluding holidays):\n\nsp = pd.read_csv(\n '../data/sp500.csv', index_col='date', parse_dates=True\n).drop(columns=['adj_close'])\n\nsp.head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3770530000\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3764890000\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3761820000\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3339890000\nFriday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3217610000\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3638790000\nTuesday\n\n\n2017-01-11\n2275.320068\n2260.830078\n2268.600098\n2275.320068\n3620410000\nWednesday\n\n\n2017-01-12\n2271.780029\n2254.250000\n2271.139893\n2270.439941\n3462130000\nThursday\n\n\n2017-01-13\n2278.679932\n2271.510010\n2272.739990\n2274.639893\n3081270000\nFriday\n\n\n2017-01-17\n2272.080078\n2262.810059\n2269.139893\n2267.889893\n3584990000\nTuesday\n\n\n\n\n\n\n\nIf we want to look at the value of a portfolio (group of assets) that trade on different days, we need to handle the mismatch in the index. Bitcoin, for example, trades daily. If we sum up all the data we have for each day (aggregations will be covered in chapter 4, so don’t fixate on this part), we get the following:\n\nbitcoin = pd.read_csv(\n '../data/bitcoin.csv', index_col='date', parse_dates=True\n).drop(columns=['market_cap'])\n\n# every day's closing price = S&P 500 close + Bitcoin close (same for other metrics)\nportfolio = pd.concat([sp, bitcoin], sort=False).groupby(level='date').sum()\n\nportfolio.head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\n1003.080000\n958.700000\n963.660000\n998.330000\n147775008\nSunday\n\n\n2017-01-02\n1031.390000\n996.700000\n998.620000\n1021.750000\n222184992\nMonday\n\n\n2017-01-03\n3307.959883\n3266.729883\n3273.170068\n3301.670078\n3955698000\nTuesday\n\n\n2017-01-04\n3432.240068\n3306.000098\n3306.000098\n3425.480000\n4109835984\nWednesday\n\n\n2017-01-05\n3462.600000\n3170.869951\n3424.909932\n3282.380000\n4272019008\nThursday\n\n\n2017-01-06\n3328.910098\n3148.000059\n3285.379893\n3179.179980\n3691766000\nFriday\n\n\n2017-01-07\n908.590000\n823.560000\n903.490000\n908.590000\n279550016\nSaturday\n\n\n2017-01-08\n942.720000\n887.250000\n908.170000\n911.200000\n158715008\nSunday\n\n\n2017-01-09\n3189.179990\n3148.709902\n3186.830088\n3171.729902\n3359486992\nMonday\n\n\n2017-01-10\n3194.140020\n3166.330020\n3172.159971\n3176.579902\n3754598000\nTuesday\n\n\n\n\n\n\n\nIt may not be immediately obvious what is wrong with the previous data, but with a visualization we can easily see the cyclical pattern of drops on the days the stock market is closed. (Don’t worry about the plotting code too much, we will cover it in depth in chapters 5 and 6).\nWe will need to import matplotlib now:\n\nimport matplotlib.pyplot as plt # we use this module for plotting\nfrom matplotlib.ticker import StrMethodFormatter # for formatting the axis\n\nNow we can see why we need to reindex:\n\n# plot the closing price from Q4 2017 through Q2 2018\nax = portfolio['2017-Q4':'2018-Q2'].plot(\n y='close', figsize=(15, 5), legend=False,\n title='Bitcoin + S&P 500 value without accounting for different indices'\n)\n\n# formatting\nax.set_ylabel('price')\nax.yaxis.set_major_formatter(StrMethodFormatter('${x:,.0f}'))\nfor spine in ['top', 'right']:\n ax.spines[spine].set_visible(False)\n\n# show the plot\nplt.show()\n\n\n\n\n\n\n\n\nWe need to align the index of the S&P 500 to match bitcoin in order to fix this. We will use the reindex() method, but by default we get NaN for the values that we don’t have data for:\n\nsp.reindex(bitcoin.index).head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\nNaN\nNaN\nNaN\nNaN\nNaN\nSunday\n\n\n2017-01-02\nNaN\nNaN\nNaN\nNaN\nNaN\nMonday\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3.770530e+09\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3.764890e+09\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3.761820e+09\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nFriday\n\n\n2017-01-07\nNaN\nNaN\nNaN\nNaN\nNaN\nSaturday\n\n\n2017-01-08\nNaN\nNaN\nNaN\nNaN\nNaN\nSunday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3.217610e+09\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3.638790e+09\nTuesday\n\n\n\n\n\n\n\nSo now we have rows for every day of the year, but all the weekends and holidays have NaN values. To address this, we can specify how to handle missing values with the method argument. In this case, we want to forward-fill, which will put the weekend and holiday values as the value they had for the Friday (or end of trading week) before:\n\nsp.reindex(bitcoin.index, method='ffill').head(10)\\\n .assign(day_of_week=lambda x: x.index.day_name())\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\nNaN\nNaN\nNaN\nNaN\nNaN\nSunday\n\n\n2017-01-02\nNaN\nNaN\nNaN\nNaN\nNaN\nMonday\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3.770530e+09\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3.764890e+09\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3.761820e+09\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nFriday\n\n\n2017-01-07\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nSaturday\n\n\n2017-01-08\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nSunday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3.217610e+09\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3.638790e+09\nTuesday\n\n\n\n\n\n\n\nTo isolate the changes happening with the forward-filling, we can use the compare() method. It shows us the values that differ across identically-labeled dataframes (same names and same columns). Here, we can see that only weekends and holidays (Monday, January 16, 2017 was MLK day) have values forward-filled. Notice that consecutive days have the same values.\n\nsp.reindex(bitcoin.index)\\\n .compare(sp.reindex(bitcoin.index, method='ffill'))\\\n .head(10).assign(day_of_week=lambda x: x.index.day_name())\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\n\nself\nother\nself\nother\nself\nother\nself\nother\nself\nother\n\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n2017-01-07\nNaN\n2282.100098\nNaN\n2264.060059\nNaN\n2271.139893\nNaN\n2276.979980\nNaN\n3.339890e+09\nSaturday\n\n\n2017-01-08\nNaN\n2282.100098\nNaN\n2264.060059\nNaN\n2271.139893\nNaN\n2276.979980\nNaN\n3.339890e+09\nSunday\n\n\n2017-01-14\nNaN\n2278.679932\nNaN\n2271.510010\nNaN\n2272.739990\nNaN\n2274.639893\nNaN\n3.081270e+09\nSaturday\n\n\n2017-01-15\nNaN\n2278.679932\nNaN\n2271.510010\nNaN\n2272.739990\nNaN\n2274.639893\nNaN\n3.081270e+09\nSunday\n\n\n2017-01-16\nNaN\n2278.679932\nNaN\n2271.510010\nNaN\n2272.739990\nNaN\n2274.639893\nNaN\n3.081270e+09\nMonday\n\n\n2017-01-21\nNaN\n2276.959961\nNaN\n2265.010010\nNaN\n2269.959961\nNaN\n2271.310059\nNaN\n3.524970e+09\nSaturday\n\n\n2017-01-22\nNaN\n2276.959961\nNaN\n2265.010010\nNaN\n2269.959961\nNaN\n2271.310059\nNaN\n3.524970e+09\nSunday\n\n\n2017-01-28\nNaN\n2299.020020\nNaN\n2291.620117\nNaN\n2299.020020\nNaN\n2294.689941\nNaN\n3.135890e+09\nSaturday\n\n\n2017-01-29\nNaN\n2299.020020\nNaN\n2291.620117\nNaN\n2299.020020\nNaN\n2294.689941\nNaN\n3.135890e+09\nSunday\n\n\n2017-02-04\nNaN\n2298.310059\nNaN\n2287.879883\nNaN\n2288.540039\nNaN\n2297.419922\nNaN\n3.597970e+09\nSaturday\n\n\n\n\n\n\n\nThis isn’t perfect though. We probably want 0 for the volume traded and to put the closing price for the open, high, low, and close on the days the market is closed:\nThe reason why we’re using np.where(boolean condition, value if True, value if False) within lambda functions in the example below, is that vectorized operations allow us to be faster and more efficient than utilizing for loops to perform calculations on arrays all at once.\n\nimport numpy as np\n\nsp_reindexed = sp.reindex(bitcoin.index).assign(\n volume=lambda x: x.volume.fillna(0), # put 0 when market is closed\n close=lambda x: x.close.fillna(method='ffill'), # carry this forward\n # take the closing price if these aren't available\n open=lambda x: np.where(x.open.isnull(), x.close, x.open),\n high=lambda x: np.where(x.high.isnull(), x.close, x.high),\n low=lambda x: np.where(x.low.isnull(), x.close, x.low)\n)\nsp_reindexed.head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\nNaN\nNaN\nNaN\nNaN\n0.000000e+00\nSunday\n\n\n2017-01-02\nNaN\nNaN\nNaN\nNaN\n0.000000e+00\nMonday\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3.770530e+09\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3.764890e+09\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3.761820e+09\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nFriday\n\n\n2017-01-07\n2276.979980\n2276.979980\n2276.979980\n2276.979980\n0.000000e+00\nSaturday\n\n\n2017-01-08\n2276.979980\n2276.979980\n2276.979980\n2276.979980\n0.000000e+00\nSunday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3.217610e+09\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3.638790e+09\nTuesday\n\n\n\n\n\n\n\nIf we create a visualization comparing the reindexed data to the first attempt, we see how reindexing helped maintain the asset value when the market was closed:\n\n# every day's closing price = S&P 500 close adjusted for market closure + Bitcoin close (same for other metrics)\nfixed_portfolio = sp_reindexed + bitcoin\n\n# plot the reindexed portfolio's closing price from Q4 2017 through Q2 2018\nax = fixed_portfolio['2017-Q4':'2018-Q2'].plot(\n y='close', label='reindexed portfolio of S&P 500 + Bitcoin', figsize=(15, 5), linewidth=2, \n title='Reindexed portfolio vs. portfolio with mismatched indices'\n)\n\n# add line for original portfolio for comparison\nportfolio['2017-Q4':'2018-Q2'].plot(\n y='close', ax=ax, linestyle='--', label='portfolio of S&P 500 + Bitcoin w/o reindexing'\n)\n\n# formatting\nax.set_ylabel('price')\nax.yaxis.set_major_formatter(StrMethodFormatter('${x:,.0f}'))\nfor spine in ['top', 'right']:\n ax.spines[spine].set_visible(False)\n\n# show the plot\nplt.show()", + "crumbs": [ + "Home", + "Cleaning Data" + ] + }, + { + "objectID": "CONTRIBUTING.html", + "href": "CONTRIBUTING.html", + "title": "Contributing to Cary’s Introduction to Python Repository", + "section": "", + "text": "Thank you for taking the time to contribute to this project. We’re excited that you’re taking the time to become involved in this project.\n\n\n\nDemonstrations of Python packages\nEnriching documentation to existing files\n\n\n\n\nBefore we get started, here are a few things we expect from you (and that you should expect from others):\n\nBe kind and thoughtful in your conversations around this project. We all come from different backgrounds and projects, which means we likely have different perspectives on “how open source is done.” Try to listen to others rather than convince them that your way is correct.\nWhen adding content, please consider if it is widely valuable. Please don’t add references or links to things you or your employer have created, as others will do so if they appreciate it.\n\n\n\n\nIf you’d like to contribute, start by searching through the pull requests to see whether someone else has raised a similar idea or question.\nIf you don’t see your idea listed, and you think it fits into the goals of this guide, open a pull request.\n\n\n\nDiscussions about the Open Source Guides take place on this repository’s Issues and Pull Requests sections. Anybody is welcome to join these conversations.\nWherever possible, do not take these conversations to private channels, including contacting the maintainers directly. Keeping communication public means everybody can benefit and learn from the conversation." + }, + { + "objectID": "CONTRIBUTING.html#types-of-contributions-were-looking-for", + "href": "CONTRIBUTING.html#types-of-contributions-were-looking-for", + "title": "Contributing to Cary’s Introduction to Python Repository", + "section": "", + "text": "Demonstrations of Python packages\nEnriching documentation to existing files" + }, + { + "objectID": "CONTRIBUTING.html#ground-rules-expectations", + "href": "CONTRIBUTING.html#ground-rules-expectations", + "title": "Contributing to Cary’s Introduction to Python Repository", + "section": "", + "text": "Before we get started, here are a few things we expect from you (and that you should expect from others):\n\nBe kind and thoughtful in your conversations around this project. We all come from different backgrounds and projects, which means we likely have different perspectives on “how open source is done.” Try to listen to others rather than convince them that your way is correct.\nWhen adding content, please consider if it is widely valuable. Please don’t add references or links to things you or your employer have created, as others will do so if they appreciate it." + }, + { + "objectID": "CONTRIBUTING.html#how-to-contribute", + "href": "CONTRIBUTING.html#how-to-contribute", + "title": "Contributing to Cary’s Introduction to Python Repository", + "section": "", + "text": "If you’d like to contribute, start by searching through the pull requests to see whether someone else has raised a similar idea or question.\nIf you don’t see your idea listed, and you think it fits into the goals of this guide, open a pull request." + }, + { + "objectID": "CONTRIBUTING.html#community", + "href": "CONTRIBUTING.html#community", + "title": "Contributing to Cary’s Introduction to Python Repository", + "section": "", + "text": "Discussions about the Open Source Guides take place on this repository’s Issues and Pull Requests sections. Anybody is welcome to join these conversations.\nWherever possible, do not take these conversations to private channels, including contacting the maintainers directly. Keeping communication public means everybody can benefit and learn from the conversation." + }, + { + "objectID": "index.html", + "href": "index.html", + "title": "Cary Introduction to Python & Anaconda", + "section": "", + "text": "For an Introduction to Data Analysis and a Refresher on Statistics\n\nintroduction_to_data_analysis.ipynb\n\nFor an Introduction to Hypothesis Testing\n\nhypothesis_testing.ipynb\n\nFor an Introduction to Python\n\npython_101.ipynb\n\nHandling Common Errors in Python\n\npython_errors.ipynb\n\nReading Local Files with pandas and visualizing datasets with matplotlib\n\nreading_local_files.ipynb\n\nMaking DataFrames from API requests\n\nmaking_dataframes_from_api_requests.ipynb\n\nWhat is wide vs long format data?\n\nwide_vs_long.ipynb\n\nCleaning data in pandas\n\ncleaning_data.ipynb\n\nHandling Data Issues in pandas\n\nhandling_data_issues.ipynb\n\nIntroduction to plotly.express\n\nintro_to_plotly_express.ipynb\n\nIntroduction to matplotlib\n\nintroducing_matplotlib.ipynb\n\nPlotting with plot() method for pandas objects\n\nplotting_with_pandas.ipynb\n\nIntroduction to pandas.plotting() module\n\npandas_plotting_module.ipynb\n\n\n\n\n\nYou can install project dependencies either using out-of-the-box conda CLI commands, or installing conda-lock to ensure dependencies are solved no matter the platform you are on.\nconda install -c conda-forge conda-lock\nconda-lock install --name name-of-your-environment conda-lock.yml\nconda activate name-of-your-environment\nconda env create -n name-of-your-environment --file environment.yml\nconda activate name-of-your-environment\n\n\n\n\nInstall your module using conda install name-of-module in your terminal or Anaconda Prompt\nInstall your module with Anaconda Navigator\n\nOpen Anaconda Navigator\nClick Environments tab\nSelect the Environment you want to install a module into\n\n\nPlease don’t use python -m pip install name-of-module when installing packages without activating your conda environment via conda activate name-of-environment first.\nAnaconda’s Explanation of conda & pip if you want a more in-depth explanation.\n\n\n\n\nGetting Started with Anaconda\nconda Basics\nJupyter Notebook Basics\n\n\n\n\n\nAnaconda Ecosystem Documentation\n\n\n\n\nAnaconda Navigator: Getting Started\nAnaconda Navigator Documentation\n\n\n\nIf your list of “Not Installed” packages is blank, I recommend manually updating Anaconda Navigator.\n\n\n\n\n\nconda: Getting Started\nconda Documenation\nconda Cheat Sheet\n\n\n\nIf you are experimenting with your conda base environment and need to restore a previous version of a conda.\n\nYou can use the conda list command with the --revisions flag to view your conda revision history.\nYou can use the conda install command with the --revision flag with the number that corresponds to the version you want to rollback to.\n\nbash or Powershell conda list --revisions conda install --revision N # Replace N with the number that corresponds to the version you want to rollback to.\n\n\n\n\nAnaconda: Conda is Fast Now. Like, Really Fast\nconda-libmamba-solver: Getting Started\nReplace it with mamba; even for existing conda installations\n\n\n\n\n\n\n\n\n\nPython.org’s ‘Whetting Your Appetite’\nPython.org’s Official Python 3 Tutorial\nPython.org’s Glossary of Terms\nPython.org’s Style Guide for Python Code\n\n\n\n\n\n\n\nFor in-memory analysis of tabular data and introduces the DataFrame for larger-than-memory datasets and want an API similar to pandas.\n\nLanding Page for polars\n\n\n\n\nFor in-memory analysis of tabular data and introduces the DataFrame\n\nLanding page for pandas\npandas Compared to Spreadsheets\npandas Compared to SQL Queries\npandas Compared to SAS\n\n\n\n\nIf you need your DataFrame to be mapped or do geospatial calculations.\n\nLanding page for geopandas\ngeopandas User Guide\n\n\n\n\nIf you need more interactivity from your generated map.\n\nLanding page for folium\n\n\n\n\nIf you need more control over your generated static map projection.\n\nLanding page for cartopy\n\n\n\n\nIf you need your dataset to have more than two dimensions.\n\nLanding Page for xarray\n\n\n\n\nHighly configurable visualization library that other libraries build off of.\n\nLanding page for matplotlib\n\n\n\n\nHigh-level library for generating statistical graphics, especially for long data format.\n\nLanding page for seaborn\n\n\n\n\nGenerate interactive graphics, with a focus on exploratory analysis with visuals.\n\nLanding page for plotly.express\n\n\n\n\n\nI highly recommend going through the official Python 3 tutorial first. It’s a great way to get your feet wet and get a feel for the language. However, here are some books I recommend if you want to go deeper or explore certain topics.\n\nAutomate the Boring Stuff with Python by Al Sweigart\nData Analysis with Pandas: 2nd Edition by Stefanie Molin\nData Science at the Command Line: 2nd Edition by Jeoroen Janssens\n\n\n\n\n\nRob Mulla on YouTube for Data Science with Focus on Python\n\nPlaylist: Medallion Python Data Science Coding Videos\nPlaylist: Working with Data in Python\n\nConference Talk: So you want to be a Python expert?\nConference Talk: 1000x faster data manipulation: vectorizing with pandas and numpy\nConference Talk: No More Sad Pandas: Optimizing Pandas Code for Sped and Efficiency\nConference Talk: Effective Pandas\nConference Tutorial: So You Wanna Be a Pandas Expert?\n\n\n\n\n\nInstalling Anaconda’s Package & Environment Manager conda (Command Line Interface Tool) and Anaconda-Navigator (Graphical User Interface Tool) (Best practice when it comes to dependency management for Python and R)\nPicking an editor of your choice that supports JupyterNotebooks (Visual Studio Code or JupyterLab)\nHow do I get data into Python and get descriptive statistics? (reading files with pandas)\nNow paint me a picture with the data. (introduction to Plotly & Holoviz Ecosystem)\nHow do I share this?\n\nBinder if you want interactivity (a little more setup)\nnbviewer if you value sharing your rendered files (less setup but not as pretty)\n\n\n\n\n\n\nHow to work with the Terminal\n\nUsing Powershell\n\nBasics of version control using Git & pushing to Github\n\nOfficial Git Documentation & Cheatsheets\nGit & Github Crash Course for Beginners\nWant Richer Change History for Notebooks? Try ReviewNB\n\n\n\n\n\nIf you have anything you want to cover, I’m open to suggestions. Feel free to checkout the contributing guidelines for ways to offer feedback and contribute. My previous experience with python covers web scraping, cleaning data, statistical analysis, and moving data into and out of databases." + }, + { + "objectID": "index.html#navigating-the-file-tree-in-this-repository", + "href": "index.html#navigating-the-file-tree-in-this-repository", + "title": "Cary Introduction to Python & Anaconda", + "section": "", + "text": "For an Introduction to Data Analysis and a Refresher on Statistics\n\nintroduction_to_data_analysis.ipynb\n\nFor an Introduction to Hypothesis Testing\n\nhypothesis_testing.ipynb\n\nFor an Introduction to Python\n\npython_101.ipynb\n\nHandling Common Errors in Python\n\npython_errors.ipynb\n\nReading Local Files with pandas and visualizing datasets with matplotlib\n\nreading_local_files.ipynb\n\nMaking DataFrames from API requests\n\nmaking_dataframes_from_api_requests.ipynb\n\nWhat is wide vs long format data?\n\nwide_vs_long.ipynb\n\nCleaning data in pandas\n\ncleaning_data.ipynb\n\nHandling Data Issues in pandas\n\nhandling_data_issues.ipynb\n\nIntroduction to plotly.express\n\nintro_to_plotly_express.ipynb\n\nIntroduction to matplotlib\n\nintroducing_matplotlib.ipynb\n\nPlotting with plot() method for pandas objects\n\nplotting_with_pandas.ipynb\n\nIntroduction to pandas.plotting() module\n\npandas_plotting_module.ipynb" + }, + { + "objectID": "index.html#installing-this-repositorys-depencies-using-conda-lock.yml-or-environment.yml-files", + "href": "index.html#installing-this-repositorys-depencies-using-conda-lock.yml-or-environment.yml-files", + "title": "Cary Introduction to Python & Anaconda", + "section": "", + "text": "You can install project dependencies either using out-of-the-box conda CLI commands, or installing conda-lock to ensure dependencies are solved no matter the platform you are on.\nconda install -c conda-forge conda-lock\nconda-lock install --name name-of-your-environment conda-lock.yml\nconda activate name-of-your-environment\nconda env create -n name-of-your-environment --file environment.yml\nconda activate name-of-your-environment" + }, + { + "objectID": "index.html#missing-an-imported-module", + "href": "index.html#missing-an-imported-module", + "title": "Cary Introduction to Python & Anaconda", + "section": "", + "text": "Install your module using conda install name-of-module in your terminal or Anaconda Prompt\nInstall your module with Anaconda Navigator\n\nOpen Anaconda Navigator\nClick Environments tab\nSelect the Environment you want to install a module into\n\n\nPlease don’t use python -m pip install name-of-module when installing packages without activating your conda environment via conda activate name-of-environment first.\nAnaconda’s Explanation of conda & pip if you want a more in-depth explanation." + }, + { + "objectID": "index.html#anacondas-free-with-account-creation-courses", + "href": "index.html#anacondas-free-with-account-creation-courses", + "title": "Cary Introduction to Python & Anaconda", + "section": "", + "text": "Getting Started with Anaconda\nconda Basics\nJupyter Notebook Basics" + }, + { + "objectID": "index.html#anaconda-ecosystem", + "href": "index.html#anaconda-ecosystem", + "title": "Cary Introduction to Python & Anaconda", + "section": "", + "text": "Anaconda Ecosystem Documentation\n\n\n\n\nAnaconda Navigator: Getting Started\nAnaconda Navigator Documentation\n\n\n\nIf your list of “Not Installed” packages is blank, I recommend manually updating Anaconda Navigator.\n\n\n\n\n\nconda: Getting Started\nconda Documenation\nconda Cheat Sheet\n\n\n\nIf you are experimenting with your conda base environment and need to restore a previous version of a conda.\n\nYou can use the conda list command with the --revisions flag to view your conda revision history.\nYou can use the conda install command with the --revision flag with the number that corresponds to the version you want to rollback to.\n\nbash or Powershell conda list --revisions conda install --revision N # Replace N with the number that corresponds to the version you want to rollback to.\n\n\n\n\nAnaconda: Conda is Fast Now. Like, Really Fast\nconda-libmamba-solver: Getting Started\nReplace it with mamba; even for existing conda installations" + }, + { + "objectID": "index.html#python-3", + "href": "index.html#python-3", + "title": "Cary Introduction to Python & Anaconda", + "section": "", + "text": "Python.org’s ‘Whetting Your Appetite’\nPython.org’s Official Python 3 Tutorial\nPython.org’s Glossary of Terms\nPython.org’s Style Guide for Python Code" + }, + { + "objectID": "index.html#python-modules", + "href": "index.html#python-modules", + "title": "Cary Introduction to Python & Anaconda", + "section": "", + "text": "For in-memory analysis of tabular data and introduces the DataFrame for larger-than-memory datasets and want an API similar to pandas.\n\nLanding Page for polars\n\n\n\n\nFor in-memory analysis of tabular data and introduces the DataFrame\n\nLanding page for pandas\npandas Compared to Spreadsheets\npandas Compared to SQL Queries\npandas Compared to SAS\n\n\n\n\nIf you need your DataFrame to be mapped or do geospatial calculations.\n\nLanding page for geopandas\ngeopandas User Guide\n\n\n\n\nIf you need more interactivity from your generated map.\n\nLanding page for folium\n\n\n\n\nIf you need more control over your generated static map projection.\n\nLanding page for cartopy\n\n\n\n\nIf you need your dataset to have more than two dimensions.\n\nLanding Page for xarray\n\n\n\n\nHighly configurable visualization library that other libraries build off of.\n\nLanding page for matplotlib\n\n\n\n\nHigh-level library for generating statistical graphics, especially for long data format.\n\nLanding page for seaborn\n\n\n\n\nGenerate interactive graphics, with a focus on exploratory analysis with visuals.\n\nLanding page for plotly.express" + }, + { + "objectID": "index.html#recommended-books", + "href": "index.html#recommended-books", + "title": "Cary Introduction to Python & Anaconda", + "section": "", + "text": "I highly recommend going through the official Python 3 tutorial first. It’s a great way to get your feet wet and get a feel for the language. However, here are some books I recommend if you want to go deeper or explore certain topics.\n\nAutomate the Boring Stuff with Python by Al Sweigart\nData Analysis with Pandas: 2nd Edition by Stefanie Molin\nData Science at the Command Line: 2nd Edition by Jeoroen Janssens" + }, + { + "objectID": "index.html#youtube-resources-to-check-out", + "href": "index.html#youtube-resources-to-check-out", + "title": "Cary Introduction to Python & Anaconda", + "section": "", + "text": "Rob Mulla on YouTube for Data Science with Focus on Python\n\nPlaylist: Medallion Python Data Science Coding Videos\nPlaylist: Working with Data in Python\n\nConference Talk: So you want to be a Python expert?\nConference Talk: 1000x faster data manipulation: vectorizing with pandas and numpy\nConference Talk: No More Sad Pandas: Optimizing Pandas Code for Sped and Efficiency\nConference Talk: Effective Pandas\nConference Tutorial: So You Wanna Be a Pandas Expert?" + }, + { + "objectID": "index.html#topics-on-the-table", + "href": "index.html#topics-on-the-table", + "title": "Cary Introduction to Python & Anaconda", + "section": "", + "text": "Installing Anaconda’s Package & Environment Manager conda (Command Line Interface Tool) and Anaconda-Navigator (Graphical User Interface Tool) (Best practice when it comes to dependency management for Python and R)\nPicking an editor of your choice that supports JupyterNotebooks (Visual Studio Code or JupyterLab)\nHow do I get data into Python and get descriptive statistics? (reading files with pandas)\nNow paint me a picture with the data. (introduction to Plotly & Holoviz Ecosystem)\nHow do I share this?\n\nBinder if you want interactivity (a little more setup)\nnbviewer if you value sharing your rendered files (less setup but not as pretty)" + }, + { + "objectID": "index.html#sidequests", + "href": "index.html#sidequests", + "title": "Cary Introduction to Python & Anaconda", + "section": "", + "text": "How to work with the Terminal\n\nUsing Powershell\n\nBasics of version control using Git & pushing to Github\n\nOfficial Git Documentation & Cheatsheets\nGit & Github Crash Course for Beginners\nWant Richer Change History for Notebooks? Try ReviewNB" + }, + { + "objectID": "index.html#contributing", + "href": "index.html#contributing", + "title": "Cary Introduction to Python & Anaconda", + "section": "", + "text": "If you have anything you want to cover, I’m open to suggestions. Feel free to checkout the contributing guidelines for ways to offer feedback and contribute. My previous experience with python covers web scraping, cleaning data, statistical analysis, and moving data into and out of databases." + }, + { + "objectID": "notebooks/handling_data_issues.html", + "href": "notebooks/handling_data_issues.html", + "title": "Handling duplicate, missing, or invalid data", + "section": "", + "text": "In this notebook, we will using daily weather data that was taken from the National Centers for Environmental Information (NCEI) API and altered to introduce many common problems faced when working with data.\nNote: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.\n\n\n\nData meanings: - PRCP: precipitation in millimeters - SNOW: snowfall in millimeters - SNWD: snow depth in millimeters - TMAX: maximum daily temperature in Celsius - TMIN: minimum daily temperature in Celsius - TOBS: temperature at time of observation in Celsius - WESF: water equivalent of snow in millimeters\nSome important facts to get our bearings: - According to the National Weather Service, the coldest temperature ever recorded in Central Park was -15°F (-26.1°C) on February 9, 1934: source - The temperature of the Sun’s photosphere is approximately 5,505°C: source\n\n\n\nWe need to import pandas and read in the dirty data to get started:\n\nimport pandas as pd\n\ndf = pd.read_csv('../data/dirty_data.csv')\n\n\n\n\nA good first step is to look at some rows:\n\ndf.head()\n\n\n\n\n\n\n\n\ndate\nstation\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n0\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n1\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n3\n2018-01-02T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\nNaN\nFalse\n\n\n4\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n\n\n\n\n\nLooking at summary statistics can reveal strange or missing values:\n\ndf.describe()\n\nc:\\Users\\gpower\\AppData\\Local\\mambaforge\\envs\\cary_dev\\Lib\\site-packages\\numpy\\lib\\function_base.py:4573: RuntimeWarning: invalid value encountered in subtract\n diff_b_a = subtract(b, a)\n\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\n\n\n\n\ncount\n765.000000\n577.000000\n577.0\n765.000000\n765.000000\n398.000000\n11.000000\n\n\nmean\n5.360392\n4.202773\nNaN\n2649.175294\n-15.914379\n8.632161\n16.290909\n\n\nstd\n10.002138\n25.086077\nNaN\n2744.156281\n24.242849\n9.815054\n9.489832\n\n\nmin\n0.000000\n0.000000\n-inf\n-11.700000\n-40.000000\n-16.100000\n1.800000\n\n\n25%\n0.000000\n0.000000\nNaN\n13.300000\n-40.000000\n0.150000\n8.600000\n\n\n50%\n0.000000\n0.000000\nNaN\n32.800000\n-11.100000\n8.300000\n19.300000\n\n\n75%\n5.800000\n0.000000\nNaN\n5505.000000\n6.700000\n18.300000\n24.900000\n\n\nmax\n61.700000\n229.000000\ninf\n5505.000000\n23.900000\n26.100000\n28.700000\n\n\n\n\n\n\n\nThe info() method can pinpoint missing values and wrong data types:\n\ndf.info()\n\n<class 'pandas.core.frame.DataFrame'>\nRangeIndex: 765 entries, 0 to 764\nData columns (total 10 columns):\n # Column Non-Null Count Dtype \n--- ------ -------------- ----- \n 0 date 765 non-null object \n 1 station 765 non-null object \n 2 PRCP 765 non-null float64\n 3 SNOW 577 non-null float64\n 4 SNWD 577 non-null float64\n 5 TMAX 765 non-null float64\n 6 TMIN 765 non-null float64\n 7 TOBS 398 non-null float64\n 8 WESF 11 non-null float64\n 9 inclement_weather 408 non-null object \ndtypes: float64(7), object(3)\nmemory usage: 59.9+ KB\n\n\nWe can use the isna()/isnull() method of the series to find nulls:\n\ncontain_nulls = df[\n df.SNOW.isna() | df.SNWD.isna() | df.TOBS.isna()\n | df.WESF.isna() | df.inclement_weather.isna()\n]\ncontain_nulls.shape[0]\n\n765\n\n\n\ncontain_nulls.head(10)\n\n\n\n\n\n\n\n\ndate\nstation\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n0\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n1\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n3\n2018-01-02T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\nNaN\nFalse\n\n\n4\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n5\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n6\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n7\n2018-01-04T00:00:00\n?\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n8\n2018-01-04T00:00:00\n?\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n9\n2018-01-05T00:00:00\n?\n0.3\nNaN\nNaN\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n\n\n\n\n\nNote that we can’t check if we have NaN like this:\n\ndf[df.inclement_weather == 'NaN'].shape[0]\n\n0\n\n\nThis is because it is actually np.nan. However, notice this also doesn’t work:\n\nimport numpy as np\ndf[df.inclement_weather == np.nan].shape[0]\n\n0\n\n\nWe have to use one of the methods discussed earlier for this to work:\n\ndf[df.inclement_weather.isna()].shape[0]\n\n357\n\n\nWe can find -inf/inf by comparing to -np.inf/np.inf:\n\ndf[df.SNWD.isin([-np.inf, np.inf])].shape[0]\n\n577\n\n\nRather than do this for each column, we can write a function that will use a dictionary comprehension to check all the columns for us:\n\ndef get_inf_count(df):\n \"\"\"Find the number of inf/-inf values per column in the dataframe\"\"\"\n return {\n col: df[df[col].isin([np.inf, -np.inf])].shape[0] for col in df.columns\n }\n\nget_inf_count(df)\n\n{'date': 0,\n 'station': 0,\n 'PRCP': 0,\n 'SNOW': 0,\n 'SNWD': 577,\n 'TMAX': 0,\n 'TMIN': 0,\n 'TOBS': 0,\n 'WESF': 0,\n 'inclement_weather': 0}\n\n\nBefore we can decide how to handle the infinite values of snow depth, we should look at the summary statistics for snowfall, which forms a big part in determining the snow depth:\n\npd.DataFrame({\n 'np.inf Snow Depth': df[df.SNWD == np.inf].SNOW.describe(),\n '-np.inf Snow Depth': df[df.SNWD == -np.inf].SNOW.describe()\n}).T\n\n\n\n\n\n\n\n\ncount\nmean\nstd\nmin\n25%\n50%\n75%\nmax\n\n\n\n\nnp.inf Snow Depth\n24.0\n101.041667\n74.498018\n13.0\n25.0\n120.5\n152.0\n229.0\n\n\n-np.inf Snow Depth\n553.0\n0.000000\n0.000000\n0.0\n0.0\n0.0\n0.0\n0.0\n\n\n\n\n\n\n\nLet’s now look into the date and station columns. We saw the ? for station earlier, so we know that was the other unique value. However, we see that some dates are present 8 times in the data and we only have 324 days meaning we are also missing days:\n\ndf.describe(include='object')\n\n\n\n\n\n\n\n\ndate\nstation\ninclement_weather\n\n\n\n\ncount\n765\n765\n408\n\n\nunique\n324\n2\n2\n\n\ntop\n2018-07-05T00:00:00\nGHCND:USC00280907\nFalse\n\n\nfreq\n8\n398\n384\n\n\n\n\n\n\n\nWe can use the duplicated() method to find duplicate rows:\n\ndf[df.duplicated()].shape[0]\n\n284\n\n\nThe default for keep is 'first' meaning it won’t show the first row that the duplicated data was seen in; we can pass in False to see it though:\n\ndf[df.duplicated(keep=False)].shape[0]\n\n482\n\n\nWe can also specify the columns to use:\n\ndf[df.duplicated(['date', 'station'])].shape[0]\n\n284\n\n\nLet’s look at a few duplicates. Just in the few values we see here, we know that the top 4 are actually in the data 6 times because by default we aren’t seeing their first occurrence:\n\ndf[df.duplicated()].head()\n\n\n\n\n\n\n\n\ndate\nstation\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n1\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n5\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n6\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n8\n2018-01-04T00:00:00\n?\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n\n\n\n\n\n\n\n\n\n\nSince we know we have NY weather data and noticed we only had two entries for station, we may decide to drop the station column because we are only interested in the weather data. However, when dealing with duplicate data, we need to think of the ramifications of removing it. Notice we only have data for the WESF column when the station is ?:\n\ndf[df.WESF.notna()].station.unique()\n\narray(['?'], dtype=object)\n\n\nIf we determine it won’t impact our analysis, we can use drop_duplicates() to remove them:\n\n# 1. make the date a datetime\ndf.date = pd.to_datetime(df.date)\n\n# 2. save this information for later\nstation_qm_wesf = df[df.station == '?'].drop_duplicates('date').set_index('date').WESF\n\n# 3. sort ? to the bottom\ndf.sort_values('station', ascending=False, inplace=True)\n\n# 4. drop duplicates based on the date column keeping the first occurrence \n# which will be the valid station if it has data\ndf_deduped = df.drop_duplicates('date')\n\n# 5. remove the station column because we are done with it\ndf_deduped = df_deduped.drop(columns='station').set_index('date').sort_index()\n\n# 6. take valid station's WESF and fall back on station ? if it is null\ndf_deduped = df_deduped.assign(\n WESF=lambda x: x.WESF.combine_first(station_qm_wesf)\n)\n\ndf_deduped.shape\n\n(324, 8)\n\n\nHere we used the combine_first() method to coalesce the values to the first non-null entry; this means that if we had data from both stations, we would first take the value provided by the named station and if (and only if) that station was null would we take the value from the station named ?. The following table contains some examples of how this would play out:\n\n\n\nstation GHCND:USC00280907\nstation ?\nresult of combine_first()\n\n\n\n\n1\n17\n1\n\n\n1\nNaN\n1\n\n\nNaN\n17\n17\n\n\nNaN\nNaN\nNaN\n\n\n\nCheck out the 4th row—we have WESF in the correct spot thanks to the index:\n\ndf_deduped.head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\nNaN\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\nNaN\nTrue\n\n\n\n\n\n\n\n\n\n\nWe could drop nulls, replace them with some arbitrary value, or impute them using the surrounding data. Each of these options may have ramifications, so we must choose wisely.\nWe can use dropna() to drop rows where any column has a null value. The default options leave us hardly any data:\n\ndf_deduped.dropna().shape\n\n(4, 8)\n\n\nIf we pass how='all', we can choose to only drop rows where everything is null, but this removes nothing:\n\ndf_deduped.dropna(how='all').shape\n\n(324, 8)\n\n\nWe can use just a subset of columns to determine what to drop with the subset argument:\n\ndf_deduped.dropna(\n how='all', subset=['inclement_weather', 'SNOW', 'SNWD']\n).shape\n\n(293, 8)\n\n\nThis can also be performed along columns, and we can also require a certain number of null values before we drop the data:\n\ndf_deduped.dropna(axis='columns', thresh=df_deduped.shape[0] * .75).columns\n\nIndex(['PRCP', 'SNOW', 'SNWD', 'TMAX', 'TMIN', 'TOBS', 'inclement_weather'], dtype='object')\n\n\nWe can choose to fill in the null values instead with fillna():\n\ndf_deduped.loc[:,'WESF'].fillna(0, inplace=True)\ndf_deduped.head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nAt this point we have done everything we can without distorting the data. We know that we are missing dates, but if we reindex, we don’t know how to fill in the NaN data. With the weather data, we can’t assume because it snowed one day that it will snow the next or that the temperature will be the same. For this reason, note that the next few examples are just for illustrative purposes only—just because we can do something doesn’t mean we should.\nThat being said, let’s try to address some of remaining issues with the temperature data. We know that when TMAX is the temperature of the Sun, it must be because there was no measured value, so let’s replace it with NaN. We will also do so for TMIN which currently uses -40°C for its placeholder when we know that the coldest temperature ever recorded in NYC was -15°F (-26.1°C) on February 9, 1934:\n\ndf_deduped = df_deduped.assign(\n TMAX=lambda x: x.TMAX.replace(5505, np.nan),\n TMIN=lambda x: x.TMIN.replace(-40, np.nan),\n)\n\nWe will also make an assumption that the temperature won’t change drastically day-to-day. Note that this is actually a big assumption, but it will allow us to understand how fillna() works when we provide a strategy through the method parameter. The fillna() method gives us 2 options for the method parameter: - 'ffill' to forward-fill - 'bfill' to back-fill\nNote that 'nearest' is missing because we are not reindexing.\nHere, we will use 'ffill' to show how this works:\n\ndf_deduped.assign(\n TMAX=lambda x: x.TMAX.fillna(method='ffill'),\n TMIN=lambda x: x.TMIN.fillna(method='ffill')\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n-4.4\n-13.9\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nWe can use np.nan_to_num() to turn np.nan into 0 and -np.inf/np.inf into large negative or positive finite numbers:\n\ndf_deduped.assign(\n SNWD=lambda x: np.nan_to_num(x.SNWD)\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-1.797693e+308\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-1.797693e+308\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-1.797693e+308\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\n1.797693e+308\nNaN\nNaN\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\n1.797693e+308\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nDepending on the data we are working with, we can use the clip() method as an alternative to np.nan_to_num(). The clip() method makes it possible to cap values at a specific minimum and/or maximum threshold. Since SNWD can’t be negative, let’s use clip() to enforce a lower bound of zero. To show how the upper bound works, let’s use the value of SNOW:\n\ndf_deduped.assign(\n SNWD=lambda x: x.SNWD.clip(0, x.SNOW)\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n0.0\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n0.0\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n0.0\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\n229.0\nNaN\nNaN\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\n127.0\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nWe can couple fillna() with other types of calculations. Here we replace missing values of TMAX with the median of all TMAX values, TMIN with the median of all TMIN values, and TOBS to the average of the TMAX and TMIN values. Since we place TOBS last, we have access to the imputed values for TMIN and TMAX in the calculation:\n\ndf_deduped.assign(\n TMAX=lambda x: x.TMAX.fillna(x.TMAX.median()),\n TMIN=lambda x: x.TMIN.fillna(x.TMIN.median()),\n # average of TMAX and TMIN\n TOBS=lambda x: x.TOBS.fillna((x.TMAX + x.TMIN) / 2)\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\n14.4\n5.6\n10.0\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n14.4\n5.6\n10.0\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nWe can also use apply() for running the same calculation across columns. For example, let’s fill all missing values with their rolling 7-day median of their values, setting the number of periods required for the calculation to 0 to ensure we don’t introduce more extra NaN values. Rolling calculations will be covered later on, so this is a preview:\n\ndf_deduped.apply(\n # rolling calculations will be covered later on, this is a rolling 7-day median\n # we set min_periods (# of periods required for calculation) to 0 so we always get a result \n lambda x: x.fillna(x.rolling(7, min_periods=0).median())\n).head(10)\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.30\n-16.1\n-12.20\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.40\n-13.9\n-13.30\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n-6.35\n-15.0\n-12.75\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.40\n-13.9\n-13.90\n0.0\nTrue\n\n\n2018-01-06\n0.0\n0.0\n-inf\n-10.00\n-15.6\n-15.00\n0.0\nFalse\n\n\n2018-01-07\n0.0\n0.0\n-inf\n-11.70\n-17.2\n-16.10\n0.0\nFalse\n\n\n2018-01-08\n0.0\n0.0\n-inf\n-7.80\n-16.7\n-8.30\n0.0\nFalse\n\n\n2018-01-10\n0.0\n0.0\n-inf\n5.00\n-7.8\n-7.80\n0.0\nFalse\n\n\n2018-01-11\n0.0\n0.0\n-inf\n4.40\n-7.8\n1.10\n0.0\nFalse\n\n\n\n\n\n\n\nThe last strategy we could try is interpolation with the interpolate() method. We specify the method parameter with the interpolation strategy to use. There are many options, but we will stick with the default of 'linear', which will treat values as evenly spaced and place missing values in the middle of existing ones. We have some missing data, so we will reindex first. Look at January 9th, which we didn’t have before—the values for TMAX, TMIN, and TOBS are the average of values the day prior (January 8th) and the day after (January 10th):\n\ndf_deduped\\\n .reindex(pd.date_range('2018-01-01', '2018-12-31', freq='D'))\\\n .apply(lambda x: x.interpolate())\\\n .head(10)\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.10\n-12.20\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.90\n-13.30\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n-4.4\n-13.90\n-13.60\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.90\n-13.90\n0.0\nTrue\n\n\n2018-01-06\n0.0\n0.0\n-inf\n-10.0\n-15.60\n-15.00\n0.0\nFalse\n\n\n2018-01-07\n0.0\n0.0\n-inf\n-11.7\n-17.20\n-16.10\n0.0\nFalse\n\n\n2018-01-08\n0.0\n0.0\n-inf\n-7.8\n-16.70\n-8.30\n0.0\nFalse\n\n\n2018-01-09\n0.0\n0.0\n-inf\n-1.4\n-12.25\n-8.05\n0.0\nNaN\n\n\n2018-01-10\n0.0\n0.0\n-inf\n5.0\n-7.80\n-7.80\n0.0\nFalse", + "crumbs": [ + "Home", + "Handling duplicate, missing, or invalid data" + ] + }, + { + "objectID": "notebooks/handling_data_issues.html#about-the-data", + "href": "notebooks/handling_data_issues.html#about-the-data", + "title": "Handling duplicate, missing, or invalid data", + "section": "", + "text": "In this notebook, we will using daily weather data that was taken from the National Centers for Environmental Information (NCEI) API and altered to introduce many common problems faced when working with data.\nNote: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.", + "crumbs": [ + "Home", + "Handling duplicate, missing, or invalid data" + ] + }, + { + "objectID": "notebooks/handling_data_issues.html#background-on-the-data", + "href": "notebooks/handling_data_issues.html#background-on-the-data", + "title": "Handling duplicate, missing, or invalid data", + "section": "", + "text": "Data meanings: - PRCP: precipitation in millimeters - SNOW: snowfall in millimeters - SNWD: snow depth in millimeters - TMAX: maximum daily temperature in Celsius - TMIN: minimum daily temperature in Celsius - TOBS: temperature at time of observation in Celsius - WESF: water equivalent of snow in millimeters\nSome important facts to get our bearings: - According to the National Weather Service, the coldest temperature ever recorded in Central Park was -15°F (-26.1°C) on February 9, 1934: source - The temperature of the Sun’s photosphere is approximately 5,505°C: source", + "crumbs": [ + "Home", + "Handling duplicate, missing, or invalid data" + ] + }, + { + "objectID": "notebooks/handling_data_issues.html#setup", + "href": "notebooks/handling_data_issues.html#setup", + "title": "Handling duplicate, missing, or invalid data", + "section": "", + "text": "We need to import pandas and read in the dirty data to get started:\n\nimport pandas as pd\n\ndf = pd.read_csv('../data/dirty_data.csv')", + "crumbs": [ + "Home", + "Handling duplicate, missing, or invalid data" + ] + }, + { + "objectID": "notebooks/handling_data_issues.html#finding-problematic-data", + "href": "notebooks/handling_data_issues.html#finding-problematic-data", + "title": "Handling duplicate, missing, or invalid data", + "section": "", + "text": "A good first step is to look at some rows:\n\ndf.head()\n\n\n\n\n\n\n\n\ndate\nstation\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n0\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n1\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n3\n2018-01-02T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\nNaN\nFalse\n\n\n4\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n\n\n\n\n\nLooking at summary statistics can reveal strange or missing values:\n\ndf.describe()\n\nc:\\Users\\gpower\\AppData\\Local\\mambaforge\\envs\\cary_dev\\Lib\\site-packages\\numpy\\lib\\function_base.py:4573: RuntimeWarning: invalid value encountered in subtract\n diff_b_a = subtract(b, a)\n\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\n\n\n\n\ncount\n765.000000\n577.000000\n577.0\n765.000000\n765.000000\n398.000000\n11.000000\n\n\nmean\n5.360392\n4.202773\nNaN\n2649.175294\n-15.914379\n8.632161\n16.290909\n\n\nstd\n10.002138\n25.086077\nNaN\n2744.156281\n24.242849\n9.815054\n9.489832\n\n\nmin\n0.000000\n0.000000\n-inf\n-11.700000\n-40.000000\n-16.100000\n1.800000\n\n\n25%\n0.000000\n0.000000\nNaN\n13.300000\n-40.000000\n0.150000\n8.600000\n\n\n50%\n0.000000\n0.000000\nNaN\n32.800000\n-11.100000\n8.300000\n19.300000\n\n\n75%\n5.800000\n0.000000\nNaN\n5505.000000\n6.700000\n18.300000\n24.900000\n\n\nmax\n61.700000\n229.000000\ninf\n5505.000000\n23.900000\n26.100000\n28.700000\n\n\n\n\n\n\n\nThe info() method can pinpoint missing values and wrong data types:\n\ndf.info()\n\n<class 'pandas.core.frame.DataFrame'>\nRangeIndex: 765 entries, 0 to 764\nData columns (total 10 columns):\n # Column Non-Null Count Dtype \n--- ------ -------------- ----- \n 0 date 765 non-null object \n 1 station 765 non-null object \n 2 PRCP 765 non-null float64\n 3 SNOW 577 non-null float64\n 4 SNWD 577 non-null float64\n 5 TMAX 765 non-null float64\n 6 TMIN 765 non-null float64\n 7 TOBS 398 non-null float64\n 8 WESF 11 non-null float64\n 9 inclement_weather 408 non-null object \ndtypes: float64(7), object(3)\nmemory usage: 59.9+ KB\n\n\nWe can use the isna()/isnull() method of the series to find nulls:\n\ncontain_nulls = df[\n df.SNOW.isna() | df.SNWD.isna() | df.TOBS.isna()\n | df.WESF.isna() | df.inclement_weather.isna()\n]\ncontain_nulls.shape[0]\n\n765\n\n\n\ncontain_nulls.head(10)\n\n\n\n\n\n\n\n\ndate\nstation\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n0\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n1\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n3\n2018-01-02T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\nNaN\nFalse\n\n\n4\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n5\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n6\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n7\n2018-01-04T00:00:00\n?\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n8\n2018-01-04T00:00:00\n?\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n9\n2018-01-05T00:00:00\n?\n0.3\nNaN\nNaN\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n\n\n\n\n\nNote that we can’t check if we have NaN like this:\n\ndf[df.inclement_weather == 'NaN'].shape[0]\n\n0\n\n\nThis is because it is actually np.nan. However, notice this also doesn’t work:\n\nimport numpy as np\ndf[df.inclement_weather == np.nan].shape[0]\n\n0\n\n\nWe have to use one of the methods discussed earlier for this to work:\n\ndf[df.inclement_weather.isna()].shape[0]\n\n357\n\n\nWe can find -inf/inf by comparing to -np.inf/np.inf:\n\ndf[df.SNWD.isin([-np.inf, np.inf])].shape[0]\n\n577\n\n\nRather than do this for each column, we can write a function that will use a dictionary comprehension to check all the columns for us:\n\ndef get_inf_count(df):\n \"\"\"Find the number of inf/-inf values per column in the dataframe\"\"\"\n return {\n col: df[df[col].isin([np.inf, -np.inf])].shape[0] for col in df.columns\n }\n\nget_inf_count(df)\n\n{'date': 0,\n 'station': 0,\n 'PRCP': 0,\n 'SNOW': 0,\n 'SNWD': 577,\n 'TMAX': 0,\n 'TMIN': 0,\n 'TOBS': 0,\n 'WESF': 0,\n 'inclement_weather': 0}\n\n\nBefore we can decide how to handle the infinite values of snow depth, we should look at the summary statistics for snowfall, which forms a big part in determining the snow depth:\n\npd.DataFrame({\n 'np.inf Snow Depth': df[df.SNWD == np.inf].SNOW.describe(),\n '-np.inf Snow Depth': df[df.SNWD == -np.inf].SNOW.describe()\n}).T\n\n\n\n\n\n\n\n\ncount\nmean\nstd\nmin\n25%\n50%\n75%\nmax\n\n\n\n\nnp.inf Snow Depth\n24.0\n101.041667\n74.498018\n13.0\n25.0\n120.5\n152.0\n229.0\n\n\n-np.inf Snow Depth\n553.0\n0.000000\n0.000000\n0.0\n0.0\n0.0\n0.0\n0.0\n\n\n\n\n\n\n\nLet’s now look into the date and station columns. We saw the ? for station earlier, so we know that was the other unique value. However, we see that some dates are present 8 times in the data and we only have 324 days meaning we are also missing days:\n\ndf.describe(include='object')\n\n\n\n\n\n\n\n\ndate\nstation\ninclement_weather\n\n\n\n\ncount\n765\n765\n408\n\n\nunique\n324\n2\n2\n\n\ntop\n2018-07-05T00:00:00\nGHCND:USC00280907\nFalse\n\n\nfreq\n8\n398\n384\n\n\n\n\n\n\n\nWe can use the duplicated() method to find duplicate rows:\n\ndf[df.duplicated()].shape[0]\n\n284\n\n\nThe default for keep is 'first' meaning it won’t show the first row that the duplicated data was seen in; we can pass in False to see it though:\n\ndf[df.duplicated(keep=False)].shape[0]\n\n482\n\n\nWe can also specify the columns to use:\n\ndf[df.duplicated(['date', 'station'])].shape[0]\n\n284\n\n\nLet’s look at a few duplicates. Just in the few values we see here, we know that the top 4 are actually in the data 6 times because by default we aren’t seeing their first occurrence:\n\ndf[df.duplicated()].head()\n\n\n\n\n\n\n\n\ndate\nstation\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n1\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n5\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n6\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n8\n2018-01-04T00:00:00\n?\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue", + "crumbs": [ + "Home", + "Handling duplicate, missing, or invalid data" + ] + }, + { + "objectID": "notebooks/handling_data_issues.html#mitigating-issues", + "href": "notebooks/handling_data_issues.html#mitigating-issues", + "title": "Handling duplicate, missing, or invalid data", + "section": "", + "text": "Since we know we have NY weather data and noticed we only had two entries for station, we may decide to drop the station column because we are only interested in the weather data. However, when dealing with duplicate data, we need to think of the ramifications of removing it. Notice we only have data for the WESF column when the station is ?:\n\ndf[df.WESF.notna()].station.unique()\n\narray(['?'], dtype=object)\n\n\nIf we determine it won’t impact our analysis, we can use drop_duplicates() to remove them:\n\n# 1. make the date a datetime\ndf.date = pd.to_datetime(df.date)\n\n# 2. save this information for later\nstation_qm_wesf = df[df.station == '?'].drop_duplicates('date').set_index('date').WESF\n\n# 3. sort ? to the bottom\ndf.sort_values('station', ascending=False, inplace=True)\n\n# 4. drop duplicates based on the date column keeping the first occurrence \n# which will be the valid station if it has data\ndf_deduped = df.drop_duplicates('date')\n\n# 5. remove the station column because we are done with it\ndf_deduped = df_deduped.drop(columns='station').set_index('date').sort_index()\n\n# 6. take valid station's WESF and fall back on station ? if it is null\ndf_deduped = df_deduped.assign(\n WESF=lambda x: x.WESF.combine_first(station_qm_wesf)\n)\n\ndf_deduped.shape\n\n(324, 8)\n\n\nHere we used the combine_first() method to coalesce the values to the first non-null entry; this means that if we had data from both stations, we would first take the value provided by the named station and if (and only if) that station was null would we take the value from the station named ?. The following table contains some examples of how this would play out:\n\n\n\nstation GHCND:USC00280907\nstation ?\nresult of combine_first()\n\n\n\n\n1\n17\n1\n\n\n1\nNaN\n1\n\n\nNaN\n17\n17\n\n\nNaN\nNaN\nNaN\n\n\n\nCheck out the 4th row—we have WESF in the correct spot thanks to the index:\n\ndf_deduped.head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\nNaN\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\nNaN\nTrue\n\n\n\n\n\n\n\n\n\n\nWe could drop nulls, replace them with some arbitrary value, or impute them using the surrounding data. Each of these options may have ramifications, so we must choose wisely.\nWe can use dropna() to drop rows where any column has a null value. The default options leave us hardly any data:\n\ndf_deduped.dropna().shape\n\n(4, 8)\n\n\nIf we pass how='all', we can choose to only drop rows where everything is null, but this removes nothing:\n\ndf_deduped.dropna(how='all').shape\n\n(324, 8)\n\n\nWe can use just a subset of columns to determine what to drop with the subset argument:\n\ndf_deduped.dropna(\n how='all', subset=['inclement_weather', 'SNOW', 'SNWD']\n).shape\n\n(293, 8)\n\n\nThis can also be performed along columns, and we can also require a certain number of null values before we drop the data:\n\ndf_deduped.dropna(axis='columns', thresh=df_deduped.shape[0] * .75).columns\n\nIndex(['PRCP', 'SNOW', 'SNWD', 'TMAX', 'TMIN', 'TOBS', 'inclement_weather'], dtype='object')\n\n\nWe can choose to fill in the null values instead with fillna():\n\ndf_deduped.loc[:,'WESF'].fillna(0, inplace=True)\ndf_deduped.head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nAt this point we have done everything we can without distorting the data. We know that we are missing dates, but if we reindex, we don’t know how to fill in the NaN data. With the weather data, we can’t assume because it snowed one day that it will snow the next or that the temperature will be the same. For this reason, note that the next few examples are just for illustrative purposes only—just because we can do something doesn’t mean we should.\nThat being said, let’s try to address some of remaining issues with the temperature data. We know that when TMAX is the temperature of the Sun, it must be because there was no measured value, so let’s replace it with NaN. We will also do so for TMIN which currently uses -40°C for its placeholder when we know that the coldest temperature ever recorded in NYC was -15°F (-26.1°C) on February 9, 1934:\n\ndf_deduped = df_deduped.assign(\n TMAX=lambda x: x.TMAX.replace(5505, np.nan),\n TMIN=lambda x: x.TMIN.replace(-40, np.nan),\n)\n\nWe will also make an assumption that the temperature won’t change drastically day-to-day. Note that this is actually a big assumption, but it will allow us to understand how fillna() works when we provide a strategy through the method parameter. The fillna() method gives us 2 options for the method parameter: - 'ffill' to forward-fill - 'bfill' to back-fill\nNote that 'nearest' is missing because we are not reindexing.\nHere, we will use 'ffill' to show how this works:\n\ndf_deduped.assign(\n TMAX=lambda x: x.TMAX.fillna(method='ffill'),\n TMIN=lambda x: x.TMIN.fillna(method='ffill')\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n-4.4\n-13.9\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nWe can use np.nan_to_num() to turn np.nan into 0 and -np.inf/np.inf into large negative or positive finite numbers:\n\ndf_deduped.assign(\n SNWD=lambda x: np.nan_to_num(x.SNWD)\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-1.797693e+308\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-1.797693e+308\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-1.797693e+308\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\n1.797693e+308\nNaN\nNaN\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\n1.797693e+308\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nDepending on the data we are working with, we can use the clip() method as an alternative to np.nan_to_num(). The clip() method makes it possible to cap values at a specific minimum and/or maximum threshold. Since SNWD can’t be negative, let’s use clip() to enforce a lower bound of zero. To show how the upper bound works, let’s use the value of SNOW:\n\ndf_deduped.assign(\n SNWD=lambda x: x.SNWD.clip(0, x.SNOW)\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n0.0\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n0.0\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n0.0\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\n229.0\nNaN\nNaN\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\n127.0\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nWe can couple fillna() with other types of calculations. Here we replace missing values of TMAX with the median of all TMAX values, TMIN with the median of all TMIN values, and TOBS to the average of the TMAX and TMIN values. Since we place TOBS last, we have access to the imputed values for TMIN and TMAX in the calculation:\n\ndf_deduped.assign(\n TMAX=lambda x: x.TMAX.fillna(x.TMAX.median()),\n TMIN=lambda x: x.TMIN.fillna(x.TMIN.median()),\n # average of TMAX and TMIN\n TOBS=lambda x: x.TOBS.fillna((x.TMAX + x.TMIN) / 2)\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\n14.4\n5.6\n10.0\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n14.4\n5.6\n10.0\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nWe can also use apply() for running the same calculation across columns. For example, let’s fill all missing values with their rolling 7-day median of their values, setting the number of periods required for the calculation to 0 to ensure we don’t introduce more extra NaN values. Rolling calculations will be covered later on, so this is a preview:\n\ndf_deduped.apply(\n # rolling calculations will be covered later on, this is a rolling 7-day median\n # we set min_periods (# of periods required for calculation) to 0 so we always get a result \n lambda x: x.fillna(x.rolling(7, min_periods=0).median())\n).head(10)\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.30\n-16.1\n-12.20\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.40\n-13.9\n-13.30\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n-6.35\n-15.0\n-12.75\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.40\n-13.9\n-13.90\n0.0\nTrue\n\n\n2018-01-06\n0.0\n0.0\n-inf\n-10.00\n-15.6\n-15.00\n0.0\nFalse\n\n\n2018-01-07\n0.0\n0.0\n-inf\n-11.70\n-17.2\n-16.10\n0.0\nFalse\n\n\n2018-01-08\n0.0\n0.0\n-inf\n-7.80\n-16.7\n-8.30\n0.0\nFalse\n\n\n2018-01-10\n0.0\n0.0\n-inf\n5.00\n-7.8\n-7.80\n0.0\nFalse\n\n\n2018-01-11\n0.0\n0.0\n-inf\n4.40\n-7.8\n1.10\n0.0\nFalse\n\n\n\n\n\n\n\nThe last strategy we could try is interpolation with the interpolate() method. We specify the method parameter with the interpolation strategy to use. There are many options, but we will stick with the default of 'linear', which will treat values as evenly spaced and place missing values in the middle of existing ones. We have some missing data, so we will reindex first. Look at January 9th, which we didn’t have before—the values for TMAX, TMIN, and TOBS are the average of values the day prior (January 8th) and the day after (January 10th):\n\ndf_deduped\\\n .reindex(pd.date_range('2018-01-01', '2018-12-31', freq='D'))\\\n .apply(lambda x: x.interpolate())\\\n .head(10)\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.10\n-12.20\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.90\n-13.30\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n-4.4\n-13.90\n-13.60\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.90\n-13.90\n0.0\nTrue\n\n\n2018-01-06\n0.0\n0.0\n-inf\n-10.0\n-15.60\n-15.00\n0.0\nFalse\n\n\n2018-01-07\n0.0\n0.0\n-inf\n-11.7\n-17.20\n-16.10\n0.0\nFalse\n\n\n2018-01-08\n0.0\n0.0\n-inf\n-7.8\n-16.70\n-8.30\n0.0\nFalse\n\n\n2018-01-09\n0.0\n0.0\n-inf\n-1.4\n-12.25\n-8.05\n0.0\nNaN\n\n\n2018-01-10\n0.0\n0.0\n-inf\n5.0\n-7.80\n-7.80\n0.0\nFalse", + "crumbs": [ + "Home", + "Handling duplicate, missing, or invalid data" + ] + }, + { + "objectID": "notebooks/introducing_matplotlib.html", + "href": "notebooks/introducing_matplotlib.html", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "Pandas uses matplotlib to create visualizations. Therefore, before we learn how to plot with pandas, it’s important to understand how matplotlib works at a high-level, which is the focus of this notebook.\n\n\nIn this notebook, we will be working with 2 datasets: - Facebook’s stock price throughout 2018 (obtained using the stock_analysis package) - Earthquake data from September 18, 2018 - October 13, 2018 (obtained from the US Geological Survey (USGS) using the USGS API)\n\n\n\nWe need to import matplotlib.pyplot for plotting.\n\nimport matplotlib.pyplot as plt\nimport pandas as pd\n\n\n\n\n\nfb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)\n\nplt.plot(fb.index, fb.open)\nplt.show()\n\n\n\n\n\n\n\n\nSince we are working in a Jupyter notebook, we can use the magic command %matplotlib inline once and not have to call plt.show() for each plot.\n\n%matplotlib inline\nimport matplotlib.pyplot as plt\nimport pandas as pd\n\nfb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)\nplt.plot(fb.index, fb.open)\n\n\n\n\n\n\n\n\n\n\n\nWe can pass in a string specifying the style of the plot. This is of the form [marker][linestyle][color]. For example, we can make a black dashed line with '--k' or a red scatter plot with 'or':\n\nplt.plot('high', 'low', 'or', data=fb.head(20))\n\n\n\n\n\n\n\n\nHere are some examples of how you make a format string:\n\n\n\nMarker\nLinestyle\nColor\nFormat String\nResult\n\n\n\n\n\n-\nb\n-b\nblue solid line\n\n\n.\n\nk\n.k\nblack points\n\n\n\n--\nr\n--r\nred dashed line\n\n\no\n-\ng\no-g\ngreen solid line with circles\n\n\n\n:\nm\n:m\nmagenta dotted line\n\n\nx\n-.\nc\nx-.c\ncyan dot-dashed line with x’s\n\n\n\nNote that we can also use format strings of the form [color][marker][linestyle], but the parsing by matplotlib (in rare cases) might not be what we were aiming for. Consult the Notes section in the documentation for the complete list of options. ## Histograms\n\nquakes = pd.read_csv('../data/earthquakes.csv')\nplt.hist(quakes.query('magType == \"ml\"').mag)\n\n(array([6.400e+01, 4.450e+02, 1.137e+03, 1.853e+03, 2.114e+03, 8.070e+02,\n 2.800e+02, 9.200e+01, 9.000e+00, 2.000e+00]),\n array([-1.26 , -0.624, 0.012, 0.648, 1.284, 1.92 , 2.556, 3.192,\n 3.828, 4.464, 5.1 ]),\n <BarContainer object of 10 artists>)\n\n\n\n\n\n\n\n\n\n\n\nNotice how our assumptions of the distribution of the data can change based on the number of bins (look at the drop between the two highest peaks on the righthand plot):\n\nx = quakes.query('magType == \"ml\"').mag\nfig, axes = plt.subplots(1, 2, figsize=(10, 3))\nfor ax, bins in zip(axes, [7, 35]):\n ax.hist(x, bins=bins)\n ax.set_title(f'bins param: {bins}')\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nTop-level object that holds the other plot components.\n\nfig = plt.figure()\n\n<Figure size 640x480 with 0 Axes>\n\n\n\n\n\nIndividual plots contained within the Figure.\n\n\n\n\nSimply specify the number of rows and columns to create:\n\nfig, axes = plt.subplots(1, 2)\n\n\n\n\n\n\n\n\nAs an alternative to using plt.subplots() we can add Axes objects to the Figure object on our own. This allows for some more complex layouts, such as picture in picture:\n\nfig = plt.figure(figsize=(3, 3))\noutside = fig.add_axes([0.1, 0.1, 0.9, 0.9])\ninside = fig.add_axes([0.7, 0.7, 0.25, 0.25])\n\n\n\n\n\n\n\n\n\n\n\nWe can create subplots with varying sizes as well:\n\nfig = plt.figure(figsize=(8, 8))\ngs = fig.add_gridspec(3, 3)\ntop_left = fig.add_subplot(gs[0, 0])\nmid_left = fig.add_subplot(gs[1, 0])\ntop_right = fig.add_subplot(gs[:2, 1:])\nbottom = fig.add_subplot(gs[2,:])\n\n\n\n\n\n\n\n\n\n\n\nUse plt.savefig() to save the last created plot. To save a specific Figure object, use its savefig() method. Which supports ‘png’, ‘pdf’, ‘svg’, and ‘eps’ filetypes.\n\nfig.savefig('empty.png')\nfig.savefig('empty.pdf')\nfig.savefig('empty.svg')\nfig.savefig('empty.eps')\n\n\n\n\nIt’s important to close resources when we are done with them. We use plt.close() to do so. If we pass in nothing, it will close the last plot, but we can pass in the specific Figure object to close or say 'all' to close all Figure objects that are open. Let’s close all the Figure objects that are open with plt.close():\n\nplt.close('all')\n\n\n\n\n\n\nJust pass the figsize argument to plt.figure(). It’s a tuple of (width, height):\n\nfig = plt.figure(figsize=(10, 4))\n\n<Figure size 1000x400 with 0 Axes>\n\n\nThis can be specified when creating subplots as well:\n\nfig, axes = plt.subplots(1, 2, figsize=(10, 4))\n\n\n\n\n\n\n\n\n\n\n\nA small subset of all the available plot settings (shuffling to get a good variation of options):\n\nimport random\nimport matplotlib as mpl\n\nrcparams_list = list(mpl.rcParams.keys())\nrandom.seed(20) # make this repeatable\nrandom.shuffle(rcparams_list)\nsorted(rcparams_list[:20])\n\n['animation.convert_args',\n 'axes.edgecolor',\n 'axes.formatter.use_locale',\n 'axes.spines.right',\n 'boxplot.meanprops.markersize',\n 'boxplot.showfliers',\n 'keymap.home',\n 'lines.markerfacecolor',\n 'lines.scale_dashes',\n 'mathtext.rm',\n 'patch.force_edgecolor',\n 'savefig.facecolor',\n 'svg.fonttype',\n 'text.hinting_factor',\n 'xtick.alignment',\n 'xtick.minor.top',\n 'xtick.minor.width',\n 'ytick.left',\n 'ytick.major.left',\n 'ytick.minor.width']\n\n\nWe can check the current default figsize using rcParams:\n\nmpl.rcParams['figure.figsize']\n\n[6.4, 4.8]\n\n\nWe can also update this value to change the default (until the kernel is restarted):\n\nmpl.rcParams['figure.figsize'] = (300, 10)\nmpl.rcParams['figure.figsize']\n\n[300.0, 10.0]\n\n\nUse rcdefaults() to restore the defaults. Note this is slightly different than before because running %matplotlib inline sets a different value for figsize (see more). After we reset, we are going back to the default value of figsize before that import:\n\nmpl.rcdefaults()\nmpl.rcParams['figure.figsize']\n\n[6.4, 4.8]\n\n\nThis can also be done via pyplot:\n\nplt.rc('figure', figsize=(20, 20)) # change `figsize` default to (20, 20)\nplt.rcdefaults() # reset the default", + "crumbs": [ + "Home", + "Getting Started with Matplotlib" + ] + }, + { + "objectID": "notebooks/introducing_matplotlib.html#about-the-data", + "href": "notebooks/introducing_matplotlib.html#about-the-data", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "In this notebook, we will be working with 2 datasets: - Facebook’s stock price throughout 2018 (obtained using the stock_analysis package) - Earthquake data from September 18, 2018 - October 13, 2018 (obtained from the US Geological Survey (USGS) using the USGS API)", + "crumbs": [ + "Home", + "Getting Started with Matplotlib" + ] + }, + { + "objectID": "notebooks/introducing_matplotlib.html#setup", + "href": "notebooks/introducing_matplotlib.html#setup", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "We need to import matplotlib.pyplot for plotting.\n\nimport matplotlib.pyplot as plt\nimport pandas as pd", + "crumbs": [ + "Home", + "Getting Started with Matplotlib" + ] + }, + { + "objectID": "notebooks/introducing_matplotlib.html#plotting-lines", + "href": "notebooks/introducing_matplotlib.html#plotting-lines", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "fb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)\n\nplt.plot(fb.index, fb.open)\nplt.show()\n\n\n\n\n\n\n\n\nSince we are working in a Jupyter notebook, we can use the magic command %matplotlib inline once and not have to call plt.show() for each plot.\n\n%matplotlib inline\nimport matplotlib.pyplot as plt\nimport pandas as pd\n\nfb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)\nplt.plot(fb.index, fb.open)", + "crumbs": [ + "Home", + "Getting Started with Matplotlib" + ] + }, + { + "objectID": "notebooks/introducing_matplotlib.html#scatter-plots", + "href": "notebooks/introducing_matplotlib.html#scatter-plots", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "We can pass in a string specifying the style of the plot. This is of the form [marker][linestyle][color]. For example, we can make a black dashed line with '--k' or a red scatter plot with 'or':\n\nplt.plot('high', 'low', 'or', data=fb.head(20))\n\n\n\n\n\n\n\n\nHere are some examples of how you make a format string:\n\n\n\nMarker\nLinestyle\nColor\nFormat String\nResult\n\n\n\n\n\n-\nb\n-b\nblue solid line\n\n\n.\n\nk\n.k\nblack points\n\n\n\n--\nr\n--r\nred dashed line\n\n\no\n-\ng\no-g\ngreen solid line with circles\n\n\n\n:\nm\n:m\nmagenta dotted line\n\n\nx\n-.\nc\nx-.c\ncyan dot-dashed line with x’s\n\n\n\nNote that we can also use format strings of the form [color][marker][linestyle], but the parsing by matplotlib (in rare cases) might not be what we were aiming for. Consult the Notes section in the documentation for the complete list of options. ## Histograms\n\nquakes = pd.read_csv('../data/earthquakes.csv')\nplt.hist(quakes.query('magType == \"ml\"').mag)\n\n(array([6.400e+01, 4.450e+02, 1.137e+03, 1.853e+03, 2.114e+03, 8.070e+02,\n 2.800e+02, 9.200e+01, 9.000e+00, 2.000e+00]),\n array([-1.26 , -0.624, 0.012, 0.648, 1.284, 1.92 , 2.556, 3.192,\n 3.828, 4.464, 5.1 ]),\n <BarContainer object of 10 artists>)\n\n\n\n\n\n\n\n\n\n\n\nNotice how our assumptions of the distribution of the data can change based on the number of bins (look at the drop between the two highest peaks on the righthand plot):\n\nx = quakes.query('magType == \"ml\"').mag\nfig, axes = plt.subplots(1, 2, figsize=(10, 3))\nfor ax, bins in zip(axes, [7, 35]):\n ax.hist(x, bins=bins)\n ax.set_title(f'bins param: {bins}')", + "crumbs": [ + "Home", + "Getting Started with Matplotlib" + ] + }, + { + "objectID": "notebooks/introducing_matplotlib.html#plot-components", + "href": "notebooks/introducing_matplotlib.html#plot-components", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "Top-level object that holds the other plot components.\n\nfig = plt.figure()\n\n<Figure size 640x480 with 0 Axes>\n\n\n\n\n\nIndividual plots contained within the Figure.", + "crumbs": [ + "Home", + "Getting Started with Matplotlib" + ] + }, + { + "objectID": "notebooks/introducing_matplotlib.html#creating-subplots", + "href": "notebooks/introducing_matplotlib.html#creating-subplots", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "Simply specify the number of rows and columns to create:\n\nfig, axes = plt.subplots(1, 2)\n\n\n\n\n\n\n\n\nAs an alternative to using plt.subplots() we can add Axes objects to the Figure object on our own. This allows for some more complex layouts, such as picture in picture:\n\nfig = plt.figure(figsize=(3, 3))\noutside = fig.add_axes([0.1, 0.1, 0.9, 0.9])\ninside = fig.add_axes([0.7, 0.7, 0.25, 0.25])", + "crumbs": [ + "Home", + "Getting Started with Matplotlib" + ] + }, + { + "objectID": "notebooks/introducing_matplotlib.html#creating-plot-layouts-with-gridspec", + "href": "notebooks/introducing_matplotlib.html#creating-plot-layouts-with-gridspec", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "We can create subplots with varying sizes as well:\n\nfig = plt.figure(figsize=(8, 8))\ngs = fig.add_gridspec(3, 3)\ntop_left = fig.add_subplot(gs[0, 0])\nmid_left = fig.add_subplot(gs[1, 0])\ntop_right = fig.add_subplot(gs[:2, 1:])\nbottom = fig.add_subplot(gs[2,:])", + "crumbs": [ + "Home", + "Getting Started with Matplotlib" + ] + }, + { + "objectID": "notebooks/introducing_matplotlib.html#saving-plots", + "href": "notebooks/introducing_matplotlib.html#saving-plots", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "Use plt.savefig() to save the last created plot. To save a specific Figure object, use its savefig() method. Which supports ‘png’, ‘pdf’, ‘svg’, and ‘eps’ filetypes.\n\nfig.savefig('empty.png')\nfig.savefig('empty.pdf')\nfig.savefig('empty.svg')\nfig.savefig('empty.eps')", + "crumbs": [ + "Home", + "Getting Started with Matplotlib" + ] + }, + { + "objectID": "notebooks/introducing_matplotlib.html#cleaning-up", + "href": "notebooks/introducing_matplotlib.html#cleaning-up", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "It’s important to close resources when we are done with them. We use plt.close() to do so. If we pass in nothing, it will close the last plot, but we can pass in the specific Figure object to close or say 'all' to close all Figure objects that are open. Let’s close all the Figure objects that are open with plt.close():\n\nplt.close('all')", + "crumbs": [ + "Home", + "Getting Started with Matplotlib" + ] + }, + { + "objectID": "notebooks/introducing_matplotlib.html#additional-plotting-options", + "href": "notebooks/introducing_matplotlib.html#additional-plotting-options", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "Just pass the figsize argument to plt.figure(). It’s a tuple of (width, height):\n\nfig = plt.figure(figsize=(10, 4))\n\n<Figure size 1000x400 with 0 Axes>\n\n\nThis can be specified when creating subplots as well:\n\nfig, axes = plt.subplots(1, 2, figsize=(10, 4))\n\n\n\n\n\n\n\n\n\n\n\nA small subset of all the available plot settings (shuffling to get a good variation of options):\n\nimport random\nimport matplotlib as mpl\n\nrcparams_list = list(mpl.rcParams.keys())\nrandom.seed(20) # make this repeatable\nrandom.shuffle(rcparams_list)\nsorted(rcparams_list[:20])\n\n['animation.convert_args',\n 'axes.edgecolor',\n 'axes.formatter.use_locale',\n 'axes.spines.right',\n 'boxplot.meanprops.markersize',\n 'boxplot.showfliers',\n 'keymap.home',\n 'lines.markerfacecolor',\n 'lines.scale_dashes',\n 'mathtext.rm',\n 'patch.force_edgecolor',\n 'savefig.facecolor',\n 'svg.fonttype',\n 'text.hinting_factor',\n 'xtick.alignment',\n 'xtick.minor.top',\n 'xtick.minor.width',\n 'ytick.left',\n 'ytick.major.left',\n 'ytick.minor.width']\n\n\nWe can check the current default figsize using rcParams:\n\nmpl.rcParams['figure.figsize']\n\n[6.4, 4.8]\n\n\nWe can also update this value to change the default (until the kernel is restarted):\n\nmpl.rcParams['figure.figsize'] = (300, 10)\nmpl.rcParams['figure.figsize']\n\n[300.0, 10.0]\n\n\nUse rcdefaults() to restore the defaults. Note this is slightly different than before because running %matplotlib inline sets a different value for figsize (see more). After we reset, we are going back to the default value of figsize before that import:\n\nmpl.rcdefaults()\nmpl.rcParams['figure.figsize']\n\n[6.4, 4.8]\n\n\nThis can also be done via pyplot:\n\nplt.rc('figure', figsize=(20, 20)) # change `figsize` default to (20, 20)\nplt.rcdefaults() # reset the default", + "crumbs": [ + "Home", + "Getting Started with Matplotlib" + ] + }, + { + "objectID": "notebooks/intro_to_plotly_express.html", + "href": "notebooks/intro_to_plotly_express.html", + "title": "Cary Introduction to Python", + "section": "", + "text": "import plotly.express as px\n\ndf = px.data.gapminder().query(\"year==2007\")\ndf.columns\n\nIndex(['country', 'continent', 'year', 'lifeExp', 'pop', 'gdpPercap',\n 'iso_alpha', 'iso_num'],\n dtype='object')\ndf.describe()\n\n\n\n\n\n\n\n\nyear\nlifeExp\npop\ngdpPercap\niso_num\n\n\n\n\ncount\n142.0\n142.000000\n1.420000e+02\n142.000000\n142.000000\n\n\nmean\n2007.0\n67.007423\n4.402122e+07\n11680.071820\n425.880282\n\n\nstd\n0.0\n12.073021\n1.476214e+08\n12859.937337\n249.111541\n\n\nmin\n2007.0\n39.613000\n1.995790e+05\n277.551859\n4.000000\n\n\n25%\n2007.0\n57.160250\n4.508034e+06\n1624.842248\n209.500000\n\n\n50%\n2007.0\n71.935500\n1.051753e+07\n6124.371109\n410.000000\n\n\n75%\n2007.0\n76.413250\n3.121004e+07\n18008.835640\n636.000000\n\n\nmax\n2007.0\n82.603000\n1.318683e+09\n49357.190170\n894.000000\npx.strip(df, x='lifeExp', hover_name=\"country\")\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.strip(df, x='lifeExp', color=\"continent\", hover_name=\"country\")\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.histogram(df, x='lifeExp', color=\"continent\", hover_name=\"country\")\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.histogram(df, x='lifeExp', color=\"continent\", hover_name=\"country\", marginal=\"rug\")\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.histogram(df, x='lifeExp', y=\"pop\", color=\"continent\", hover_name=\"country\", marginal=\"rug\")\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.histogram(df, x='lifeExp', y=\"pop\", color=\"continent\", hover_name=\"country\", marginal=\"rug\", facet_col=\"continent\")\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.bar(df, color='lifeExp', x=\"pop\", y=\"continent\", hover_name=\"country\")\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.sunburst(df, color='lifeExp', values=\"pop\", path=[\"continent\", \"country\"], hover_name=\"country\", height=500)\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.treemap(df, color='lifeExp', values=\"pop\", path=[\"continent\", \"country\"], hover_name=\"country\", height=500)\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.choropleth(df, color='lifeExp', locations=\"iso_alpha\", hover_name=\"country\", height=500)\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.scatter(df, x=\"gdpPercap\", y='lifeExp', hover_name=\"country\", height=500)\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.scatter(df, x=\"gdpPercap\", y='lifeExp', hover_name=\"country\", color=\"continent\",size=\"pop\", height=500)\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\nWe can see that the curve follows a logarithmic path, so make log_x=True to straighten out the line to view the relationships in an easier manner. In the graph below we can view the monotic and nonmonotonic relationships in the dataset.\npx.scatter(df, x=\"gdpPercap\", y='lifeExp', hover_name=\"country\", color=\"continent\",size=\"pop\", size_max=60, log_x=True, height=500)\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\nfig = px.scatter(df, x=\"gdpPercap\", y='lifeExp', hover_name=\"country\", color=\"continent\",size=\"pop\", size_max=60, log_x=True, height=500)\nThis will allow you to inspect the values for each of these cells, unfortunately this is a great deal easier to see in JupyterLab.\nfig.show(\"json\")\n\nUnable to display output for mime type(s): application/json\nimport plotly.express as px\n\ndf = px.data.gapminder().query(\"year == 2007\")\n\nfig = px.scatter(df, x=\"gdpPercap\",y=\"lifeExp\", color=\"continent\", log_x=True, size=\"pop\", size_max=60,\n hover_name=\"country\", height=600, width=1000, template=\"simple_white\", \n color_discrete_sequence=px.colors.qualitative.G10,\n title=\"Health vs Wealth 2007\",\n labels=dict(\n continent=\"Continent\", pop=\"Population\",\n gdpPercap=\"GDP per Capita (US$, price-adjusted)\", \n lifeExp=\"Life Expectancy (years)\"))\n\nfig.update_layout(font_family=\"Rockwell\",\n legend=dict(orientation=\"h\", title=\"\", y=1.1, x=1, xanchor=\"right\", yanchor=\"bottom\"))\nfig.update_xaxes(tickprefix=\"$\", range=[2,5], dtick=1)\nfig.update_yaxes(range=[30,90])\nfig.add_hline((df[\"lifeExp\"]*df[\"pop\"]).sum()/df[\"pop\"].sum(), line_width=1, line_dash=\"dot\")\nfig.add_vline((df[\"gdpPercap\"]*df[\"pop\"]).sum()/df[\"pop\"].sum(), line_width=1, line_dash=\"dot\")\nfig.show()\n\n# fig.write_image(\"gapminder_2007.svg\") # static export\n# fig.write_html(\"gapminder_2007.html\") # interactive export\n# fig.write_json(\"gapminder_2007.json\") # serialized export\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json", + "crumbs": [ + "Home", + "Animations in Plotly Express" + ] + }, + { + "objectID": "notebooks/intro_to_plotly_express.html#animations-in-plotly-express", + "href": "notebooks/intro_to_plotly_express.html#animations-in-plotly-express", + "title": "Cary Introduction to Python", + "section": "Animations in Plotly Express", + "text": "Animations in Plotly Express\n\ndf_animation = px.data.gapminder()\n\nanim_fig = px.scatter(df_animation, x=\"gdpPercap\", y=\"lifeExp\",\n title=\"Health vs Wealth from 1952 to 2007\",\n labels=dict(continent=\"Continent\", pop=\"Population\", gdpPercap=\"GDP per Capita (US$, price-adjusted)\", lifeExp=\"Life Expectancy (years)\"),\n animation_frame=\"year\", animation_group=\"country\",\n size=\"pop\",\n color=\"continent\",\n hover_name=\"country\",\n height=600,width=1000,\n template=\"simple_white\",\n color_discrete_sequence=px.colors.qualitative.G10,\n log_x=True,\n size_max=60,\n range_x=[100,100000],\n range_y=[25,90])\n\nanim_fig.update_layout(font_family=\"Rockwell\",\n legend=dict(orientation=\"h\", title=\"\", y=1.1, x=1, xanchor=\"right\", yanchor=\"bottom\"))\nanim_fig.update_xaxes(tickprefix=\"$\", range=[2,5], dtick=1)\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\nanim_fig.write_html(\"gapminder_animation.html\", auto_play=False) # You're able to export this animation.\n\n\npx.defaults.height=600\n\n\nimport plotly.express as px\n\nz = [[.1, .3, .5, .7, .9],\n [1, .8, .6, .4, .2],\n [.2, 0, .5, .7, .9],\n [.9, .8, .4, .2, 0],\n [.3, .4, .5, .7, 1]]\n\nfig = px.imshow(z, text_auto=True)\nfig.show()\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\nimport plotly.express as px\ndf = px.data.wind()\nfig = px.bar_polar(df, r=\"frequency\", theta=\"direction\", height=600,\n color=\"strength\", template=\"plotly_dark\",\n color_discrete_sequence= px.colors.sequential.Plasma_r)\nfig.show()\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\ndf = px.data.iris()\nfig = px.parallel_coordinates(df, color=\"species_id\", labels={\"species_id\": \"Species\",\n \"sepal_width\": \"Sepal Width\", \"sepal_length\": \"Sepal Length\",\n \"petal_width\": \"Petal Width\", \"petal_length\": \"Petal Length\", },\n color_continuous_scale=px.colors.diverging.Tealrose, color_continuous_midpoint=2)\nfig.show()\n\nc:\\Users\\gpower\\Anaconda3\\envs\\cary_dev\\lib\\site-packages\\plotly\\express\\_core.py:279: FutureWarning:\n\niteritems is deprecated and will be removed in a future version. Use .items instead.\n\n\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\ndf = px.data.tips()\nfig = px.parallel_categories(df, color=\"size\", color_continuous_scale=px.colors.sequential.Inferno)\nfig.show()\n\nc:\\Users\\gpower\\Anaconda3\\envs\\cary_dev\\lib\\site-packages\\plotly\\express\\_core.py:279: FutureWarning:\n\niteritems is deprecated and will be removed in a future version. Use .items instead.\n\n\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\ndf = px.data.iris()\nfig = px.parallel_coordinates(df, color=\"species_id\", labels={\"species_id\": \"Species\",\n \"sepal_width\": \"Sepal Width\", \"sepal_length\": \"Sepal Length\",\n \"petal_width\": \"Petal Width\", \"petal_length\": \"Petal Length\", },\n color_continuous_scale=px.colors.diverging.Tealrose, color_continuous_midpoint=2)\nfig.show()\n\nc:\\Users\\gpower\\Anaconda3\\envs\\cary_dev\\lib\\site-packages\\plotly\\express\\_core.py:279: FutureWarning:\n\niteritems is deprecated and will be removed in a future version. Use .items instead.\n\n\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\ndf = px.data.tips()\nfig = px.parallel_categories(df, color=\"size\", color_continuous_scale=px.colors.sequential.Inferno)\nfig.show()\n\nc:\\Users\\gpower\\Anaconda3\\envs\\cary_dev\\lib\\site-packages\\plotly\\express\\_core.py:279: FutureWarning:\n\niteritems is deprecated and will be removed in a future version. Use .items instead.\n\n\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\ndf = px.data.election()\nfig = px.scatter_ternary(df, a=\"Joly\", b=\"Coderre\", c=\"Bergeron\", color=\"winner\", size=\"total\", hover_name=\"district\",\n size_max=15, color_discrete_map = {\"Joly\": \"blue\", \"Bergeron\": \"green\", \"Coderre\":\"red\"} )\nfig.show()\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\ndf = px.data.election()\nfig = px.scatter_3d(df, x=\"Joly\", y=\"Coderre\", z=\"Bergeron\", color=\"winner\", size=\"total\", hover_name=\"district\",\n symbol=\"result\", color_discrete_map = {\"Joly\": \"blue\", \"Bergeron\": \"green\", \"Coderre\":\"red\"})\nfig.show()\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json", + "crumbs": [ + "Home", + "Animations in Plotly Express" + ] + }, + { + "objectID": "notebooks/one_hot_encoding.html", + "href": "notebooks/one_hot_encoding.html", + "title": "One-Hot Encoding", + "section": "", + "text": "It’s useful for feeding categorical data into machine-learning algorithms since integers are computationally less expensive than strings.\n\nimport pandas as pd\nprint(pd.__version__)\n\n2.0.3\n\n\n\ndisengagements = pd.read_excel(\"../data/cassi-autonomous-shuttle/autonomous_shuttle_disengagement.xlsx\",usecols=[\"Incident Datetime\", \"Location\",\"Weather\",\"Vehicle Speed in Miles per Hour\", \"Initiated by\",\"Cause\"], parse_dates=True)\ndisengagements\n\n\n\n\n\n\n\n\nIncident Datetime\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\n\n\n\n\n0\n2023-03-07T10:00:00-05:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n\n\n1\n2023-03-07T14:00:00-05:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n\n\n2\n2023-03-07T14:30:00-05:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n\n\n3\n2023-03-07T15:15:00-05:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n\n\n4\n2023-03-08T10:00:00-05:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n\n\n...\n...\n...\n...\n...\n...\n...\n\n\n174\n2023-06-01T16:00:00-04:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n\n\n175\n2023-06-02T10:32:00-04:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n\n\n176\n2023-06-02T10:35:00-04:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n\n\n177\n2023-06-02T10:44:00-04:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n\n\n178\n2023-06-02T11:01:00-04:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n\n\n\n\n179 rows × 6 columns\n\n\n\n\ndisengagements.dtypes\n\nIncident Datetime object\nLocation object\nWeather object\nVehicle Speed in Miles per Hour int64\nInitiated by object\nCause object\ndtype: object\n\n\n\ndisengagements['Incident Datetime'] = pd.to_datetime(disengagements['Incident Datetime'], utc=True)\ndisengagements['Initiated by'] = disengagements['Initiated by'].astype('category')\ndisengagements['Cause'] = disengagements['Cause'].astype('category')\ndisengagements.dtypes\n\nIncident Datetime datetime64[ns, UTC]\nLocation object\nWeather object\nVehicle Speed in Miles per Hour int64\nInitiated by category\nCause category\ndtype: object\n\n\n\ndisengagements = disengagements.assign(week_of_year = disengagements['Incident Datetime'].dt.isocalendar().week, week_of_pilot = lambda x: disengagements['Incident Datetime'].dt.isocalendar().week - 9)\ndisengagements\n\n\n\n\n\n\n\n\nIncident Datetime\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\nweek_of_year\nweek_of_pilot\n\n\n\n\n0\n2023-03-07 15:00:00+00:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n10\n1\n\n\n1\n2023-03-07 19:00:00+00:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2\n2023-03-07 19:30:00+00:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n3\n2023-03-07 20:15:00+00:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n10\n1\n\n\n4\n2023-03-08 15:00:00+00:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n10\n1\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n174\n2023-06-01 20:00:00+00:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n22\n13\n\n\n175\n2023-06-02 14:32:00+00:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n22\n13\n\n\n176\n2023-06-02 14:35:00+00:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n22\n13\n\n\n177\n2023-06-02 14:44:00+00:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n22\n13\n\n\n178\n2023-06-02 15:01:00+00:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n22\n13\n\n\n\n\n179 rows × 8 columns\n\n\n\n\ndisengagements['Cause']\n\n0 Fault Code/Error Code\n1 Station Blocked\n2 Station Blocked\n3 Station Blocked\n4 Shuttle Manually Deviated from Approved Path\n ... \n174 Signal Loss\n175 Station Blocked\n176 Station Blocked\n177 Obstacle Detection\n178 Signalized Intersection\nName: Cause, Length: 179, dtype: category\nCategories (9, object): ['Fault Code/Error Code', 'Obstacle Detection', 'Other Road Users', 'Priority Zone', ..., 'Signal Loss', 'Signalized Intersection', 'Station Blocked', 'Vegetation']\n\n\n\ndisengagements['Cause'].cat.categories\n\nIndex(['Fault Code/Error Code', 'Obstacle Detection', 'Other Road Users',\n 'Priority Zone', 'Shuttle Manually Deviated from Approved Path',\n 'Signal Loss', 'Signalized Intersection', 'Station Blocked',\n 'Vegetation'],\n dtype='object')\n\n\n\ndisengagements_datetime_is_index = disengagements.set_index('Incident Datetime')\ndisengagements_datetime_is_index\n\n\n\n\n\n\n\n\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\nweek_of_year\nweek_of_pilot\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 15:00:00+00:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n10\n1\n\n\n2023-03-07 19:00:00+00:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 19:30:00+00:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 20:15:00+00:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-08 15:00:00+00:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n10\n1\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 20:00:00+00:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n22\n13\n\n\n2023-06-02 14:32:00+00:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 14:35:00+00:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 14:44:00+00:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n22\n13\n\n\n2023-06-02 15:01:00+00:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n22\n13\n\n\n\n\n179 rows × 7 columns\n\n\n\n\ndisengagements_datetime_is_index.index=disengagements_datetime_is_index.index.tz_convert(tz='US/Eastern')\ndisengagements_datetime_is_index\n\n\n\n\n\n\n\n\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\nweek_of_year\nweek_of_pilot\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n10\n1\n\n\n2023-03-07 14:00:00-05:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 14:30:00-05:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 15:15:00-05:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-08 10:00:00-05:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n10\n1\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n22\n13\n\n\n2023-06-02 10:32:00-04:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 10:35:00-04:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 10:44:00-04:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n22\n13\n\n\n2023-06-02 11:01:00-04:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n22\n13\n\n\n\n\n179 rows × 7 columns\n\n\n\n\ndisengagements_datetime_is_index.dtypes\n\nLocation object\nWeather object\nVehicle Speed in Miles per Hour int64\nInitiated by category\nCause category\nweek_of_year UInt32\nweek_of_pilot UInt32\ndtype: object\n\n\n\none_hot = disengagements_datetime_is_index.Weather.str.get_dummies(sep=';')\none_hot\n\n\n\n\n\n\n\n\nCloudy\nPartly Cloudy\nRain\nSunny\nWindy\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:32:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 11:01:00-04:00\n0\n0\n0\n1\n0\n\n\n\n\n179 rows × 5 columns\n\n\n\n\none_hot.columns = 'Weather_' + one_hot.columns\none_hot\n\n\n\n\n\n\n\n\nWeather_Cloudy\nWeather_Partly Cloudy\nWeather_Rain\nWeather_Sunny\nWeather_Windy\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:32:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 11:01:00-04:00\n0\n0\n0\n1\n0\n\n\n\n\n179 rows × 5 columns\n\n\n\n\none_hot_cause = disengagements_datetime_is_index.Cause.str.get_dummies()\none_hot_cause.columns = 'Cause_' + one_hot_cause.columns\none_hot_cause\n\n\n\n\n\n\n\n\nCause_Fault Code/Error Code\nCause_Obstacle Detection\nCause_Other Road Users\nCause_Priority Zone\nCause_Shuttle Manually Deviated from Approved Path\nCause_Signal Loss\nCause_Signalized Intersection\nCause_Station Blocked\nCause_Vegetation\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n1\n0\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-03-07 14:00:00-05:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n0\n0\n0\n0\n1\n0\n0\n0\n0\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n0\n0\n0\n0\n0\n1\n0\n0\n0\n\n\n2023-06-02 10:32:00-04:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n0\n1\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-06-02 11:01:00-04:00\n0\n0\n0\n0\n0\n0\n1\n0\n0\n\n\n\n\n179 rows × 9 columns\n\n\n\n\ndisengagements_datetime_is_index = disengagements_datetime_is_index.drop(['Weather', 'Initiated by', 'Cause'], axis=1)\ncassi_data_one_hot_encoded = pd.concat([disengagements_datetime_is_index, one_hot, one_hot_cause], axis=1)\ncassi_data_one_hot_encoded\n\n\n\n\n\n\n\n\nLocation\nVehicle Speed in Miles per Hour\nweek_of_year\nweek_of_pilot\nWeather_Cloudy\nWeather_Partly Cloudy\nWeather_Rain\nWeather_Sunny\nWeather_Windy\nCause_Fault Code/Error Code\nCause_Obstacle Detection\nCause_Other Road Users\nCause_Priority Zone\nCause_Shuttle Manually Deviated from Approved Path\nCause_Signal Loss\nCause_Signalized Intersection\nCause_Station Blocked\nCause_Vegetation\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n35.7849964, -78.8268094\n2\n10\n1\n0\n0\n0\n1\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-03-07 14:00:00-05:00\n35.7847312, -78.8245051\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n35.7824658, -78.8244159\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n35.7824658, -78.8244159\n4\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n35.7852558, -78.8273737\n2\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n1\n0\n0\n0\n0\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n35.783456, -78.821639\n5\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n1\n0\n0\n0\n\n\n2023-06-02 10:32:00-04:00\n35.7819145, -78.8235603\n4\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n35.7813188, -78.8256601\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n35.7847325, -78.824496\n4\n22\n13\n0\n0\n0\n1\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-06-02 11:01:00-04:00\n35.7841086, -78.8261962\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n1\n0\n0\n\n\n\n\n179 rows × 18 columns\n\n\n\n\ncassi_data_one_hot_encoded.index = cassi_data_one_hot_encoded.index.tz_convert(tz='UTC')\n\n\ncassi_data_one_hot_encoded\n\n\n\n\n\n\n\n\nLocation\nVehicle Speed in Miles per Hour\nweek_of_year\nweek_of_pilot\nWeather_Cloudy\nWeather_Partly Cloudy\nWeather_Rain\nWeather_Sunny\nWeather_Windy\nCause_Fault Code/Error Code\nCause_Obstacle Detection\nCause_Other Road Users\nCause_Priority Zone\nCause_Shuttle Manually Deviated from Approved Path\nCause_Signal Loss\nCause_Signalized Intersection\nCause_Station Blocked\nCause_Vegetation\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 15:00:00+00:00\n35.7849964, -78.8268094\n2\n10\n1\n0\n0\n0\n1\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-03-07 19:00:00+00:00\n35.7847312, -78.8245051\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 19:30:00+00:00\n35.7824658, -78.8244159\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 20:15:00+00:00\n35.7824658, -78.8244159\n4\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-08 15:00:00+00:00\n35.7852558, -78.8273737\n2\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n1\n0\n0\n0\n0\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 20:00:00+00:00\n35.783456, -78.821639\n5\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n1\n0\n0\n0\n\n\n2023-06-02 14:32:00+00:00\n35.7819145, -78.8235603\n4\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 14:35:00+00:00\n35.7813188, -78.8256601\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 14:44:00+00:00\n35.7847325, -78.824496\n4\n22\n13\n0\n0\n0\n1\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-06-02 15:01:00+00:00\n35.7841086, -78.8261962\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n1\n0\n0\n\n\n\n\n179 rows × 18 columns" + }, + { + "objectID": "notebooks/one_hot_encoding.html#why-bother-with-one-hot-encoding", + "href": "notebooks/one_hot_encoding.html#why-bother-with-one-hot-encoding", + "title": "One-Hot Encoding", + "section": "", + "text": "It’s useful for feeding categorical data into machine-learning algorithms since integers are computationally less expensive than strings.\n\nimport pandas as pd\nprint(pd.__version__)\n\n2.0.3\n\n\n\ndisengagements = pd.read_excel(\"../data/cassi-autonomous-shuttle/autonomous_shuttle_disengagement.xlsx\",usecols=[\"Incident Datetime\", \"Location\",\"Weather\",\"Vehicle Speed in Miles per Hour\", \"Initiated by\",\"Cause\"], parse_dates=True)\ndisengagements\n\n\n\n\n\n\n\n\nIncident Datetime\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\n\n\n\n\n0\n2023-03-07T10:00:00-05:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n\n\n1\n2023-03-07T14:00:00-05:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n\n\n2\n2023-03-07T14:30:00-05:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n\n\n3\n2023-03-07T15:15:00-05:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n\n\n4\n2023-03-08T10:00:00-05:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n\n\n...\n...\n...\n...\n...\n...\n...\n\n\n174\n2023-06-01T16:00:00-04:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n\n\n175\n2023-06-02T10:32:00-04:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n\n\n176\n2023-06-02T10:35:00-04:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n\n\n177\n2023-06-02T10:44:00-04:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n\n\n178\n2023-06-02T11:01:00-04:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n\n\n\n\n179 rows × 6 columns\n\n\n\n\ndisengagements.dtypes\n\nIncident Datetime object\nLocation object\nWeather object\nVehicle Speed in Miles per Hour int64\nInitiated by object\nCause object\ndtype: object\n\n\n\ndisengagements['Incident Datetime'] = pd.to_datetime(disengagements['Incident Datetime'], utc=True)\ndisengagements['Initiated by'] = disengagements['Initiated by'].astype('category')\ndisengagements['Cause'] = disengagements['Cause'].astype('category')\ndisengagements.dtypes\n\nIncident Datetime datetime64[ns, UTC]\nLocation object\nWeather object\nVehicle Speed in Miles per Hour int64\nInitiated by category\nCause category\ndtype: object\n\n\n\ndisengagements = disengagements.assign(week_of_year = disengagements['Incident Datetime'].dt.isocalendar().week, week_of_pilot = lambda x: disengagements['Incident Datetime'].dt.isocalendar().week - 9)\ndisengagements\n\n\n\n\n\n\n\n\nIncident Datetime\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\nweek_of_year\nweek_of_pilot\n\n\n\n\n0\n2023-03-07 15:00:00+00:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n10\n1\n\n\n1\n2023-03-07 19:00:00+00:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2\n2023-03-07 19:30:00+00:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n3\n2023-03-07 20:15:00+00:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n10\n1\n\n\n4\n2023-03-08 15:00:00+00:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n10\n1\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n174\n2023-06-01 20:00:00+00:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n22\n13\n\n\n175\n2023-06-02 14:32:00+00:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n22\n13\n\n\n176\n2023-06-02 14:35:00+00:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n22\n13\n\n\n177\n2023-06-02 14:44:00+00:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n22\n13\n\n\n178\n2023-06-02 15:01:00+00:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n22\n13\n\n\n\n\n179 rows × 8 columns\n\n\n\n\ndisengagements['Cause']\n\n0 Fault Code/Error Code\n1 Station Blocked\n2 Station Blocked\n3 Station Blocked\n4 Shuttle Manually Deviated from Approved Path\n ... \n174 Signal Loss\n175 Station Blocked\n176 Station Blocked\n177 Obstacle Detection\n178 Signalized Intersection\nName: Cause, Length: 179, dtype: category\nCategories (9, object): ['Fault Code/Error Code', 'Obstacle Detection', 'Other Road Users', 'Priority Zone', ..., 'Signal Loss', 'Signalized Intersection', 'Station Blocked', 'Vegetation']\n\n\n\ndisengagements['Cause'].cat.categories\n\nIndex(['Fault Code/Error Code', 'Obstacle Detection', 'Other Road Users',\n 'Priority Zone', 'Shuttle Manually Deviated from Approved Path',\n 'Signal Loss', 'Signalized Intersection', 'Station Blocked',\n 'Vegetation'],\n dtype='object')\n\n\n\ndisengagements_datetime_is_index = disengagements.set_index('Incident Datetime')\ndisengagements_datetime_is_index\n\n\n\n\n\n\n\n\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\nweek_of_year\nweek_of_pilot\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 15:00:00+00:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n10\n1\n\n\n2023-03-07 19:00:00+00:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 19:30:00+00:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 20:15:00+00:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-08 15:00:00+00:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n10\n1\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 20:00:00+00:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n22\n13\n\n\n2023-06-02 14:32:00+00:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 14:35:00+00:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 14:44:00+00:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n22\n13\n\n\n2023-06-02 15:01:00+00:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n22\n13\n\n\n\n\n179 rows × 7 columns\n\n\n\n\ndisengagements_datetime_is_index.index=disengagements_datetime_is_index.index.tz_convert(tz='US/Eastern')\ndisengagements_datetime_is_index\n\n\n\n\n\n\n\n\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\nweek_of_year\nweek_of_pilot\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n10\n1\n\n\n2023-03-07 14:00:00-05:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 14:30:00-05:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 15:15:00-05:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-08 10:00:00-05:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n10\n1\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n22\n13\n\n\n2023-06-02 10:32:00-04:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 10:35:00-04:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 10:44:00-04:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n22\n13\n\n\n2023-06-02 11:01:00-04:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n22\n13\n\n\n\n\n179 rows × 7 columns\n\n\n\n\ndisengagements_datetime_is_index.dtypes\n\nLocation object\nWeather object\nVehicle Speed in Miles per Hour int64\nInitiated by category\nCause category\nweek_of_year UInt32\nweek_of_pilot UInt32\ndtype: object\n\n\n\none_hot = disengagements_datetime_is_index.Weather.str.get_dummies(sep=';')\none_hot\n\n\n\n\n\n\n\n\nCloudy\nPartly Cloudy\nRain\nSunny\nWindy\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:32:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 11:01:00-04:00\n0\n0\n0\n1\n0\n\n\n\n\n179 rows × 5 columns\n\n\n\n\none_hot.columns = 'Weather_' + one_hot.columns\none_hot\n\n\n\n\n\n\n\n\nWeather_Cloudy\nWeather_Partly Cloudy\nWeather_Rain\nWeather_Sunny\nWeather_Windy\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:32:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 11:01:00-04:00\n0\n0\n0\n1\n0\n\n\n\n\n179 rows × 5 columns\n\n\n\n\none_hot_cause = disengagements_datetime_is_index.Cause.str.get_dummies()\none_hot_cause.columns = 'Cause_' + one_hot_cause.columns\none_hot_cause\n\n\n\n\n\n\n\n\nCause_Fault Code/Error Code\nCause_Obstacle Detection\nCause_Other Road Users\nCause_Priority Zone\nCause_Shuttle Manually Deviated from Approved Path\nCause_Signal Loss\nCause_Signalized Intersection\nCause_Station Blocked\nCause_Vegetation\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n1\n0\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-03-07 14:00:00-05:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n0\n0\n0\n0\n1\n0\n0\n0\n0\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n0\n0\n0\n0\n0\n1\n0\n0\n0\n\n\n2023-06-02 10:32:00-04:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n0\n1\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-06-02 11:01:00-04:00\n0\n0\n0\n0\n0\n0\n1\n0\n0\n\n\n\n\n179 rows × 9 columns\n\n\n\n\ndisengagements_datetime_is_index = disengagements_datetime_is_index.drop(['Weather', 'Initiated by', 'Cause'], axis=1)\ncassi_data_one_hot_encoded = pd.concat([disengagements_datetime_is_index, one_hot, one_hot_cause], axis=1)\ncassi_data_one_hot_encoded\n\n\n\n\n\n\n\n\nLocation\nVehicle Speed in Miles per Hour\nweek_of_year\nweek_of_pilot\nWeather_Cloudy\nWeather_Partly Cloudy\nWeather_Rain\nWeather_Sunny\nWeather_Windy\nCause_Fault Code/Error Code\nCause_Obstacle Detection\nCause_Other Road Users\nCause_Priority Zone\nCause_Shuttle Manually Deviated from Approved Path\nCause_Signal Loss\nCause_Signalized Intersection\nCause_Station Blocked\nCause_Vegetation\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n35.7849964, -78.8268094\n2\n10\n1\n0\n0\n0\n1\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-03-07 14:00:00-05:00\n35.7847312, -78.8245051\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n35.7824658, -78.8244159\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n35.7824658, -78.8244159\n4\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n35.7852558, -78.8273737\n2\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n1\n0\n0\n0\n0\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n35.783456, -78.821639\n5\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n1\n0\n0\n0\n\n\n2023-06-02 10:32:00-04:00\n35.7819145, -78.8235603\n4\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n35.7813188, -78.8256601\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n35.7847325, -78.824496\n4\n22\n13\n0\n0\n0\n1\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-06-02 11:01:00-04:00\n35.7841086, -78.8261962\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n1\n0\n0\n\n\n\n\n179 rows × 18 columns\n\n\n\n\ncassi_data_one_hot_encoded.index = cassi_data_one_hot_encoded.index.tz_convert(tz='UTC')\n\n\ncassi_data_one_hot_encoded\n\n\n\n\n\n\n\n\nLocation\nVehicle Speed in Miles per Hour\nweek_of_year\nweek_of_pilot\nWeather_Cloudy\nWeather_Partly Cloudy\nWeather_Rain\nWeather_Sunny\nWeather_Windy\nCause_Fault Code/Error Code\nCause_Obstacle Detection\nCause_Other Road Users\nCause_Priority Zone\nCause_Shuttle Manually Deviated from Approved Path\nCause_Signal Loss\nCause_Signalized Intersection\nCause_Station Blocked\nCause_Vegetation\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 15:00:00+00:00\n35.7849964, -78.8268094\n2\n10\n1\n0\n0\n0\n1\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-03-07 19:00:00+00:00\n35.7847312, -78.8245051\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 19:30:00+00:00\n35.7824658, -78.8244159\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 20:15:00+00:00\n35.7824658, -78.8244159\n4\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-08 15:00:00+00:00\n35.7852558, -78.8273737\n2\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n1\n0\n0\n0\n0\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 20:00:00+00:00\n35.783456, -78.821639\n5\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n1\n0\n0\n0\n\n\n2023-06-02 14:32:00+00:00\n35.7819145, -78.8235603\n4\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 14:35:00+00:00\n35.7813188, -78.8256601\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 14:44:00+00:00\n35.7847325, -78.824496\n4\n22\n13\n0\n0\n0\n1\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-06-02 15:01:00+00:00\n35.7841086, -78.8261962\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n1\n0\n0\n\n\n\n\n179 rows × 18 columns" + }, + { + "objectID": "notebooks/plotting_with_pandas.html", + "href": "notebooks/plotting_with_pandas.html", + "title": "Plotting with Pandas", + "section": "", + "text": "The plot() method is available on Series and DataFrame objects. Many of the parameters get passed down to matplotlib. The kind argument let’s us vary the plot type. Here are some commonly used parameters:\n\n\n\n\n\n\n\n\nParameter\nPurpose\nData Type\n\n\n\n\nkind\nDetermines the plot type\nString\n\n\nx/y\nColumn(s) to plot on the x-axis/y-axis\nString or list\n\n\nax\nDraws the plot on the Axes object provided\nAxes\n\n\nsubplots\nDetermines whether to make subplots\nBoolean\n\n\nlayout\nSpecifies how to arrange the subplots\nTuple of (rows, columns)\n\n\nfigsize\nSize to make the Figure object\nTuple of (width, height)\n\n\ntitle\nThe title of the plot or subplots\nString for the plot title or a list of strings for subplot titles\n\n\nlegend\nDetermines whether to show the legend\nBoolean\n\n\nlabel\nWhat to call an item in the legend\nString if a single column is being plotted; otherwise, a list of strings\n\n\nstyle\nmatplotlib style strings for each item being plotted\nString if a single column is being plotted; otherwise, a list of strings\n\n\ncolor\nThe color to plot the item in\nString or red, green, blue tuple if a single column is being plotted; otherwise, a list\n\n\ncolormap\nThe colormap to use\nString or matplotlib colormap object\n\n\nlogx/logy/loglog\nDetermines whether to use a logarithmic scale for the x-axis, y-axis, or both\nBoolean\n\n\nxticks/yticks\nDetermines where to draw the ticks on the x-axis/y-axis\nList of values\n\n\nxlim/ylim\nThe axis limits for the x-axis/y-axis\nTuple of the form (min, max)\n\n\nrot\nThe angle to write the tick labels at\nInteger\n\n\nsharex/sharey\nDetermines whether to have subplots share the x-axis/y-axis\nBoolean\n\n\nfontsize\nControls the size of the tick labels\nInteger\n\n\ngrid\nTurns on/off the grid lines\nBoolean\n\n\n\n\n\nIn this notebook, we will be working with 3 datasets: - Facebook’s stock price throughout 2018 (obtained using the stock_analysis package) - Earthquake data from September 18, 2018 - October 13, 2018 (obtained from the US Geological Survey (USGS) using the USGS API) - European Centre for Disease Prevention and Control’s (ECDC) daily number of new reported cases of COVID-19 by country worldwide dataset collected on September 19, 2020 via this link\n\n\n\n\n%matplotlib inline\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)\nquakes = pd.read_csv('../data/earthquakes.csv')\ncovid = pd.read_csv('../data/covid19_cases.csv').assign(\n date=lambda x: pd.to_datetime(x.dateRep, format='%d/%m/%Y')\n).set_index('date').replace(\n 'United_States_of_America', 'USA'\n).sort_index()['2020-01-18':'2020-09-18']\n\n\n\n\nLine plots help us see how a variable changes over time. They are the default for the kind argument, but we can pass kind='line' to be explicit in our intent:\n\nfb.plot(\n kind='line',\n y='open',\n figsize=(10, 5),\n style='-b',\n legend=False,\n title='Evolution of Facebook Open Price'\n)\n\n\n\n\n\n\n\n\nWe provided the style argument in the previous example; however, we can use the color and linestyle arguments to get the same result:\n\nfb.plot(\n kind='line',\n y='open',\n figsize=(10, 5),\n color='blue',\n linestyle='solid',\n legend=False,\n title='Evolution of Facebook Open Price'\n)\n\n\n\n\n\n\n\n\nWe can also plot many lines at once by simply passing a list of the columns to plot:\n\nfb.first('1W').plot(\n y=['open', 'high', 'low', 'close'],\n style=['o-b', '--r', ':k', '.-g'],\n title='Facebook OHLC Prices during 1st Week of Trading 2018'\n).autoscale()\n\n\n\n\n\n\n\n\n\n\nWhen plotting with pandas, creating subplots is simply a matter of passing subplots=True to the plot() method, and (optionally) specifying the layout in a tuple of (rows, columns):\n\nfb.plot(\n kind='line',\n subplots=True,\n layout=(3, 2),\n figsize=(15, 10),\n title='Facebook Stock 2018'\n)\n\narray([[<Axes: xlabel='date'>, <Axes: xlabel='date'>],\n [<Axes: xlabel='date'>, <Axes: xlabel='date'>],\n [<Axes: xlabel='date'>, <Axes: xlabel='date'>]], dtype=object)\n\n\n\n\n\n\n\n\n\nNote that we didn’t provide a specific column to plot and pandas plotted all of them for us.\nSometimes we want to make subplots that each have a few variables in them for comparison. This can be achieved using the ax parameter. To illustrate this, let’s take a look at daily new COVID-19 cases in China, Spain, Italy, the USA, Brazil, and India:\n\nnew_cases_rolling_average = covid.pivot_table(\n index=covid.index, \n columns='countriesAndTerritories', \n values='cases'\n).rolling(7).mean()\n\nSince there is a lot of fluctuation in these values, we will plot the 7-day moving average of new cases using the rolling() method (discussed in chapter 4). Rather than create a separate plot for each country (which makes it harder to compare) or plot them all together (which will make it difficult to see the smaller values), we will plot countries that have had a similar number of cases in the same subplot:\n\nfig, axes = plt.subplots(1, 3, figsize=(15, 5))\n\nnew_cases_rolling_average[['China']].plot(ax=axes[0], style='-.c')\nnew_cases_rolling_average[['Italy', 'Spain']].plot(\n ax=axes[1], style=['-', '--'], \n title='7-day rolling average of new COVID-19 cases\\n(source: ECDC)'\n)\nnew_cases_rolling_average[['Brazil', 'India', 'USA']]\\\n .plot(ax=axes[2], style=['--', ':', '-'])\n\n\n\n\n\n\n\n\nNOTE: we specified the line styles here so that the lines can be distinguished in the text as a black and white image.\nIn the previous figure, we were able to compare countries with similar levels of new COVID-19 cases, but we couldn’t compare all of them in the same plot due to scale. One way around this is to use an area plot, which makes it possible for us to visualize the overall 7-day rolling average of new COVID-19 cases and at the same time how much each country is contributing to the total. In the interest of readability, we will group Italy and Spain together and create another category for countries other than the USA, Brazil, and India. The combined height of the plot areas is the overall value, and the height of given shaded region is the value for the individual country.\n\nplot_cols = ['Brazil', 'India', 'Italy & Spain', 'USA', 'Other']\ngrouped = ['Italy', 'Spain']\nother_cols = [\n col for col in new_cases_rolling_average.columns \n if col not in plot_cols\n]\n\nnew_cases_rolling_average.sort_index(axis=1).assign(\n **{\n 'Italy & Spain': lambda x: x[grouped].sum(axis=1),\n 'Other': lambda x: x[other_cols].drop(columns=grouped).sum(axis=1)\n }\n)[plot_cols].plot(\n kind='area', figsize=(15, 5), \n title='7-day rolling average of new COVID-19 cases\\n(source: ECDC)'\n)\n\n\n\n\n\n\n\n\nAnother way to visualize evolution over time is to look at the cumulative sum over time. Let’s plot the cumulative number of COVID-19 cases in China, Spain, Italy, the USA, Brazil, and India, using ax to create subplots as we did in the previous example.\n\nfig, axes = plt.subplots(1, 3, figsize=(15, 3))\n\ncumulative_covid_cases = covid.groupby(\n ['countriesAndTerritories', pd.Grouper(freq='1D')]\n).cases.sum().unstack(0).apply('cumsum')\n\ncumulative_covid_cases[['China']].plot(ax=axes[0], style='-.c')\ncumulative_covid_cases[['Italy', 'Spain']].plot(\n ax=axes[1], style=['-', '--'], \n title='Cumulative COVID-19 Cases\\n(source: ECDC)'\n)\ncumulative_covid_cases[['Brazil', 'India', 'USA']]\\\n .plot(ax=axes[2], style=['--', ':', '-'])\n\n\n\n\n\n\n\n\nNOTE: we specified the line styles here so that the lines can be distinguished in the text as a black and white image.\n\n\n\n\n\n\nWe make scatter plots to help visualize the relationship between two variables. Creating scatter plots requires we pass in kind='scatter' along with a column for the x-axis and a column for the y-axis:\n\nfb.assign(\n max_abs_change=fb.high - fb.low\n).plot(\n kind='scatter', x='volume', y='max_abs_change',\n title='Facebook Daily High - Low vs. Volume Traded'\n)\n\n\n\n\n\n\n\n\nThe relationship doesn’t seem to be linear, but we can try a log transform on the x-axis since the scales of the axes are very different. With pandas, we simply pass in logx=True:\n\nfb.assign(\n max_abs_change=fb.high - fb.low\n).plot(\n kind='scatter', x='volume', y='max_abs_change',\n title='Facebook Daily High - Low vs. log(Volume Traded)', \n logx=True\n)\n\n\n\n\n\n\n\n\nWith matplotlib, we could use plt.xscale('log') to do the same thing.\n\n\n\nSometimes our plots have many overlapping values, but this can be impossible to see. This can be addressed by increasing the transparency of what we are plotting using the alpha parameter. It is a float in the range [0, 1] where 0 is completely transparent and 1 is completely opaque. By default this is 1, so let’s put in a lower value and re-plot the scatter plot:\n\nfb.assign(\n max_abs_change=fb.high - fb.low\n).plot(\n kind='scatter', x='volume', y='max_abs_change',\n title='Facebook Daily High - Low vs. log(Volume Traded)',\n logx=True, alpha=0.25\n)\n\n\n\n\n\n\n\n\n\n\n\nIn the previous example, we can start to see the overlaps, but it is still difficult. Hexbins are another plot type that divide up the plot into hexagons, which are shaded according to the density of points there. With pandas, this is the hexbin value for the kind argument. It may also be necessary to tweak the gridsize, which determines the number of hexagons along the y-axis:\n\nfb.assign(\n log_volume=np.log(fb.volume),\n max_abs_change=fb.high - fb.low\n).plot(\n kind='hexbin',\n x='log_volume',\n y='max_abs_change',\n title='Facebook Daily High - Low vs. log(Volume Traded)',\n colormap='gray_r',\n gridsize=20, \n sharex=False # we have to pass this to see the x-axis\n)\n\n\n\n\n\n\n\n\n\n\n\nPandas doesn’t offer heatmaps; however, if we are able to get our data into a matrix, we can use matshow() from matplotlib:\n\nfig, ax = plt.subplots(figsize=(20, 10))\n\n# calculate the correlation matrix\nfb_corr = fb.assign(\n log_volume=np.log(fb.volume),\n max_abs_change=fb.high - fb.low\n).corr()\n\n# create the heatmap and colorbar\nim = ax.matshow(fb_corr, cmap='seismic')\nim.set_clim(-1, 1)\nfig.colorbar(im)\n\n# label the ticks with the column names\nlabels = [col.lower() for col in fb_corr.columns]\nax.set_xticks(ax.get_xticks()[1:-1]) # to handle bug in matplotlib\nax.set_xticklabels(labels, rotation=45)\nax.set_yticks(ax.get_yticks()[1:-1]) # to handle bug in matplotlib\nax.set_yticklabels(labels)\n\n# include the value of the correlation coefficient in the boxes\nfor (i, j), coef in np.ndenumerate(fb_corr):\n ax.text(\n i, j, fr'$\\rho$ = {coef:.2f}', # raw (r), format (f) string\n ha='center', va='center', \n color='white', fontsize=14\n )\n\n\n\n\n\n\n\n\nAccessing the values in the correlation matrix can be done with loc[]:\n\nfb_corr.loc['max_abs_change', ['volume', 'log_volume']]\n\nvolume 0.642027\nlog_volume 0.731542\nName: max_abs_change, dtype: float64\n\n\n\n\n\n\n\n\nWith the pandas, making histograms is as easy as passing kind='hist' to the plot() method:\n\nfb.volume.plot(\n kind='hist', \n title='Histogram of Daily Volume Traded in Facebook Stock'\n)\nplt.xlabel('Volume traded') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'Volume traded')\n\n\n\n\n\n\n\n\n\nWe can overlap histograms to compare distributions provided we use the alpha parameter. For example, let’s compare the usage and magnitude of the various measurement techniques (the magType column) in the data:\n\nfig, axes = plt.subplots(figsize=(8, 5))\n\nfor magtype in quakes.magType.unique():\n data = quakes.query(f'magType == \"{magtype}\"').mag\n if not data.empty:\n data.plot(\n kind='hist', ax=axes, alpha=0.4, \n label=magtype, legend=True,\n title='Comparing histograms of earthquake magnitude by magType'\n )\n\nplt.xlabel('magnitude') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'magnitude')\n\n\n\n\n\n\n\n\n\n\n\n\nWe can pass kind='kde' for an estimate of the probability density function (PDF), which tells us the probability of getting a particular value:\n\nfb.high.plot(\n kind='kde', \n title='KDE of Daily High Price for Facebook Stock'\n)\nplt.xlabel('Price ($)') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'Price ($)')\n\n\n\n\n\n\n\n\n\n\n\n\nThe plot() method returns an Axes object. We can store this for additional customization of the plot, or we can pass this into another call to plot() as the ax argument to add to the original plot.\nIt can often be helpful to view the KDE superimposed on top of the histogram, which can be achieved with this strategy:\n\nax = fb.high.plot(kind='hist', density=True, alpha=0.5)\nfb.high.plot(\n ax=ax, kind='kde', color='blue', \n title='Distribution of Facebook Stock\\'s Daily High Price in 2018'\n)\nplt.xlabel('Price ($)') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'Price ($)')\n\n\n\n\n\n\n\n\n\n\n\n\nIn some cases, we are more interested in the probability of getting less than or equal to that value (or greater than or equal), which we can see with the cumulative disribution function (CDF). Using the statsmodels package, we can estimate the CDF giving us the empirical cumulative distribution function (ECDF):\n\nfrom statsmodels.distributions.empirical_distribution import ECDF\n\necdf = ECDF(quakes.query('magType == \"ml\"').mag)\nplt.plot(ecdf.x, ecdf.y)\n\n# axis labels (we will cover this in chapter 6)\nplt.xlabel('mag') # add x-axis label \nplt.ylabel('cumulative probability') # add y-axis label\n\n# add title (we will cover this in chapter 6)\nplt.title('ECDF of earthquake magnitude with magType ml')\n\nText(0.5, 1.0, 'ECDF of earthquake magnitude with magType ml')\n\n\n\n\n\n\n\n\n\nThis ECDF tells us the probability of getting an earthquake with magnitude of 3 or less using the ml scale is 98%:\n\nfrom statsmodels.distributions.empirical_distribution import ECDF\n\necdf = ECDF(quakes.query('magType == \"ml\"').mag)\nplt.plot(ecdf.x, ecdf.y)\n\n# formatting below will all be covered in chapter 6\n# axis labels\nplt.xlabel('mag') # add x-axis label \nplt.ylabel('cumulative probability') # add y-axis label\n\n# add reference lines for interpreting the ECDF for mag <= 3 \nplt.plot(\n [3, 3], [0, .98], '--k', \n [-1.5, 3], [0.98, 0.98], '--k', alpha=0.4\n)\n\n# set axis ranges\nplt.ylim(0, None)\nplt.xlim(-1.25, None)\n\n# add a title\nplt.title('P(mag <= 3) = 98%')\n\nText(0.5, 1.0, 'P(mag <= 3) = 98%')\n\n\n\n\n\n\n\n\n\n\n\n\nTo make box plots with pandas, we pass kind='box' to the plot() method:\n\nfb.iloc[:,:4].plot(kind='box', title='Facebook OHLC Prices Box Plot')\nplt.ylabel('price ($)') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'price ($)')\n\n\n\n\n\n\n\n\n\nIf we pass in notch=True, we get a notched box plot. The notch represents a 95% confidence interval around the median, which can be helpful when comparing differences. For an introduction to interpreting a notched box plot, see this Google sites page and this Towards Data Science article.\n\nfb.iloc[:,:4].plot(kind='box', title='Facebook OHLC Prices Box Plot', notch=True)\nplt.ylabel('price ($)') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'price ($)')\n\n\n\n\n\n\n\n\n\nThis can also be combined with a call to groupby():\n\nfb.assign(\n volume_bin=pd.cut(fb.volume, 3, labels=['low', 'med', 'high'])\n).groupby('volume_bin').boxplot(\n column=['open', 'high', 'low', 'close'],\n layout=(1, 3), figsize=(12, 3)\n)\nplt.suptitle('Facebook OHLC Box Plots by Volume Traded', y=1.1)\n\nText(0.5, 1.1, 'Facebook OHLC Box Plots by Volume Traded')\n\n\n\n\n\n\n\n\n\nWe can use this to see the distribution of magnitudes across the different measurement methods for earthquakes:\n\nquakes[['mag', 'magType']].groupby('magType').boxplot(\n figsize=(15, 8), subplots=False\n)\nplt.title('Earthquake Magnitude Box Plots by magType')\nplt.ylabel('magnitude') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'magnitude')\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nPassing kind='barh' gives us horizontal bars while kind='bar' gives us vertical ones. Let’s use horizontal bars to look at the top 15 places for earthquakes in our data:\n\nquakes.parsed_place.value_counts().iloc[14::-1,].plot(\n kind='barh', figsize=(10, 5),\n title='Top 15 Places for Earthquakes '\n '(September 18, 2018 - October 13, 2018)'\n)\nplt.xlabel('earthquakes') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'earthquakes')\n\n\n\n\n\n\n\n\n\nWe also have data on whether earthquakes were accompanied by tsunamis. Let’s see what the top places for tsunamis are:\n\nquakes.groupby('parsed_place').tsunami.sum().sort_values().iloc[-10:,].plot(\n kind='barh', figsize=(10, 5), \n title='Top 10 Places for Tsunamis '\n '(September 18, 2018 - October 13, 2018)'\n)\nplt.xlabel('tsunamis') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'tsunamis')\n\n\n\n\n\n\n\n\n\nSeeing that Indonesia is the top place for tsunamis during the time period we are looking at, we may want to look how many earthquakes and tsunamis Indonesia gets on a daily basis. We could show this as a line plot or with bars; since we don’t want to interpolate, we will use bars here:\n\nindonesia_quakes = quakes.query('parsed_place == \"Indonesia\"').assign(\n time=lambda x: pd.to_datetime(x.time, unit='ms'),\n earthquake=1\n).set_index('time').resample('1D').sum()\n\n# format the datetimes in the index for the x-axis\nindonesia_quakes.index = indonesia_quakes.index.strftime('%b\\n%d')\n\nindonesia_quakes.plot(\n y=['earthquake', 'tsunami'], kind='bar', figsize=(15, 3), \n rot=0, label=['earthquakes', 'tsunamis'], \n title='Earthquakes and Tsunamis in Indonesia '\n '(September 18, 2018 - October 13, 2018)'\n)\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('date')\nplt.ylabel('count')\n\nC:\\Users\\gpower\\AppData\\Local\\Temp\\ipykernel_13112\\3940988219.py:4: FutureWarning: The default value of numeric_only in DataFrameGroupBy.sum is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.\n ).set_index('time').resample('1D').sum()\n\n\nText(0, 0.5, 'count')\n\n\n\n\n\n\n\n\n\n\n\n\n\nquakes.groupby(['parsed_place', 'tsunami']).mag.count()\\\n .unstack().apply(lambda x: x / x.sum(), axis=1)\\\n .rename(columns={0: 'no', 1: 'yes'})\\\n .sort_values('yes', ascending=False)[7::-1]\\\n .plot.barh(\n title='Frequency of a tsunami accompanying an earthquake'\n )\n\n# move legend to the right of the plot\nplt.legend(title='tsunami?', bbox_to_anchor=(1, 0.65))\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('percentage of earthquakes')\nplt.ylabel('')\n\nText(0, 0.5, '')\n\n\n\n\n\n\n\n\n\nUsing the kind arugment for vertical bars when the labels for each bar are shorter:\n\nquakes.magType.value_counts().plot(\n kind='bar', title='Earthquakes Recorded per magType', rot=0\n)\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('magType')\nplt.ylabel('earthquakes')\n\nText(0, 0.5, 'earthquakes')\n\n\n\n\n\n\n\n\n\n\n\n\n\npivot = quakes.assign(\n mag_bin=lambda x: np.floor(x.mag)\n).pivot_table(\n index='mag_bin', columns='magType', values='mag', aggfunc='count'\n)\npivot.plot.bar(\n stacked=True, rot=0, ylabel='earthquakes', \n title='Earthquakes by integer magnitude and magType'\n)\n\n\n\n\n\n\n\n\n\n\nPlot the percentages to be better able to see the different magTypes.\n\nnormalized_pivot = pivot.fillna(0).apply(lambda x: x / x.sum(), axis=1)\nax = normalized_pivot.plot.bar(\n stacked=True, rot=0, figsize=(10, 5),\n title='Percentage of earthquakes by integer magnitude for each magType'\n)\nax.legend(bbox_to_anchor=(1, 0.8)) # move legend to the right of the plot\nplt.ylabel('percentage') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'percentage')\n\n\n\n\n\n\n\n\n\nWe can also create horizontal stacked bars and do so using groupby() and unstack():\n\nquakes.groupby(['parsed_place', 'tsunami']).mag.count()\\\n .unstack().apply(lambda x: x / x.sum(), axis=1)\\\n .rename(columns={0: 'no', 1: 'yes'})\\\n .sort_values('yes', ascending=False)[7::-1]\\\n .plot.barh(\n title='Frequency of a tsunami accompanying an earthquake', \n stacked=True\n )\n\n# move legend to the right of the plot\nplt.legend(title='tsunami?', bbox_to_anchor=(1, 0.65))\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('percentage of earthquakes')\nplt.ylabel('')\n\nText(0, 0.5, '')", + "crumbs": [ + "Home", + "Plotting with Pandas" + ] + }, + { + "objectID": "notebooks/plotting_with_pandas.html#about-the-data", + "href": "notebooks/plotting_with_pandas.html#about-the-data", + "title": "Plotting with Pandas", + "section": "", + "text": "In this notebook, we will be working with 3 datasets: - Facebook’s stock price throughout 2018 (obtained using the stock_analysis package) - Earthquake data from September 18, 2018 - October 13, 2018 (obtained from the US Geological Survey (USGS) using the USGS API) - European Centre for Disease Prevention and Control’s (ECDC) daily number of new reported cases of COVID-19 by country worldwide dataset collected on September 19, 2020 via this link", + "crumbs": [ + "Home", + "Plotting with Pandas" + ] + }, + { + "objectID": "notebooks/plotting_with_pandas.html#setup", + "href": "notebooks/plotting_with_pandas.html#setup", + "title": "Plotting with Pandas", + "section": "", + "text": "%matplotlib inline\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)\nquakes = pd.read_csv('../data/earthquakes.csv')\ncovid = pd.read_csv('../data/covid19_cases.csv').assign(\n date=lambda x: pd.to_datetime(x.dateRep, format='%d/%m/%Y')\n).set_index('date').replace(\n 'United_States_of_America', 'USA'\n).sort_index()['2020-01-18':'2020-09-18']", + "crumbs": [ + "Home", + "Plotting with Pandas" + ] + }, + { + "objectID": "notebooks/plotting_with_pandas.html#evolution-over-time", + "href": "notebooks/plotting_with_pandas.html#evolution-over-time", + "title": "Plotting with Pandas", + "section": "", + "text": "Line plots help us see how a variable changes over time. They are the default for the kind argument, but we can pass kind='line' to be explicit in our intent:\n\nfb.plot(\n kind='line',\n y='open',\n figsize=(10, 5),\n style='-b',\n legend=False,\n title='Evolution of Facebook Open Price'\n)\n\n\n\n\n\n\n\n\nWe provided the style argument in the previous example; however, we can use the color and linestyle arguments to get the same result:\n\nfb.plot(\n kind='line',\n y='open',\n figsize=(10, 5),\n color='blue',\n linestyle='solid',\n legend=False,\n title='Evolution of Facebook Open Price'\n)\n\n\n\n\n\n\n\n\nWe can also plot many lines at once by simply passing a list of the columns to plot:\n\nfb.first('1W').plot(\n y=['open', 'high', 'low', 'close'],\n style=['o-b', '--r', ':k', '.-g'],\n title='Facebook OHLC Prices during 1st Week of Trading 2018'\n).autoscale()\n\n\n\n\n\n\n\n\n\n\nWhen plotting with pandas, creating subplots is simply a matter of passing subplots=True to the plot() method, and (optionally) specifying the layout in a tuple of (rows, columns):\n\nfb.plot(\n kind='line',\n subplots=True,\n layout=(3, 2),\n figsize=(15, 10),\n title='Facebook Stock 2018'\n)\n\narray([[<Axes: xlabel='date'>, <Axes: xlabel='date'>],\n [<Axes: xlabel='date'>, <Axes: xlabel='date'>],\n [<Axes: xlabel='date'>, <Axes: xlabel='date'>]], dtype=object)\n\n\n\n\n\n\n\n\n\nNote that we didn’t provide a specific column to plot and pandas plotted all of them for us.\nSometimes we want to make subplots that each have a few variables in them for comparison. This can be achieved using the ax parameter. To illustrate this, let’s take a look at daily new COVID-19 cases in China, Spain, Italy, the USA, Brazil, and India:\n\nnew_cases_rolling_average = covid.pivot_table(\n index=covid.index, \n columns='countriesAndTerritories', \n values='cases'\n).rolling(7).mean()\n\nSince there is a lot of fluctuation in these values, we will plot the 7-day moving average of new cases using the rolling() method (discussed in chapter 4). Rather than create a separate plot for each country (which makes it harder to compare) or plot them all together (which will make it difficult to see the smaller values), we will plot countries that have had a similar number of cases in the same subplot:\n\nfig, axes = plt.subplots(1, 3, figsize=(15, 5))\n\nnew_cases_rolling_average[['China']].plot(ax=axes[0], style='-.c')\nnew_cases_rolling_average[['Italy', 'Spain']].plot(\n ax=axes[1], style=['-', '--'], \n title='7-day rolling average of new COVID-19 cases\\n(source: ECDC)'\n)\nnew_cases_rolling_average[['Brazil', 'India', 'USA']]\\\n .plot(ax=axes[2], style=['--', ':', '-'])\n\n\n\n\n\n\n\n\nNOTE: we specified the line styles here so that the lines can be distinguished in the text as a black and white image.\nIn the previous figure, we were able to compare countries with similar levels of new COVID-19 cases, but we couldn’t compare all of them in the same plot due to scale. One way around this is to use an area plot, which makes it possible for us to visualize the overall 7-day rolling average of new COVID-19 cases and at the same time how much each country is contributing to the total. In the interest of readability, we will group Italy and Spain together and create another category for countries other than the USA, Brazil, and India. The combined height of the plot areas is the overall value, and the height of given shaded region is the value for the individual country.\n\nplot_cols = ['Brazil', 'India', 'Italy & Spain', 'USA', 'Other']\ngrouped = ['Italy', 'Spain']\nother_cols = [\n col for col in new_cases_rolling_average.columns \n if col not in plot_cols\n]\n\nnew_cases_rolling_average.sort_index(axis=1).assign(\n **{\n 'Italy & Spain': lambda x: x[grouped].sum(axis=1),\n 'Other': lambda x: x[other_cols].drop(columns=grouped).sum(axis=1)\n }\n)[plot_cols].plot(\n kind='area', figsize=(15, 5), \n title='7-day rolling average of new COVID-19 cases\\n(source: ECDC)'\n)\n\n\n\n\n\n\n\n\nAnother way to visualize evolution over time is to look at the cumulative sum over time. Let’s plot the cumulative number of COVID-19 cases in China, Spain, Italy, the USA, Brazil, and India, using ax to create subplots as we did in the previous example.\n\nfig, axes = plt.subplots(1, 3, figsize=(15, 3))\n\ncumulative_covid_cases = covid.groupby(\n ['countriesAndTerritories', pd.Grouper(freq='1D')]\n).cases.sum().unstack(0).apply('cumsum')\n\ncumulative_covid_cases[['China']].plot(ax=axes[0], style='-.c')\ncumulative_covid_cases[['Italy', 'Spain']].plot(\n ax=axes[1], style=['-', '--'], \n title='Cumulative COVID-19 Cases\\n(source: ECDC)'\n)\ncumulative_covid_cases[['Brazil', 'India', 'USA']]\\\n .plot(ax=axes[2], style=['--', ':', '-'])\n\n\n\n\n\n\n\n\nNOTE: we specified the line styles here so that the lines can be distinguished in the text as a black and white image.", + "crumbs": [ + "Home", + "Plotting with Pandas" + ] + }, + { + "objectID": "notebooks/plotting_with_pandas.html#visualizing-relationships-between-variables", + "href": "notebooks/plotting_with_pandas.html#visualizing-relationships-between-variables", + "title": "Plotting with Pandas", + "section": "", + "text": "We make scatter plots to help visualize the relationship between two variables. Creating scatter plots requires we pass in kind='scatter' along with a column for the x-axis and a column for the y-axis:\n\nfb.assign(\n max_abs_change=fb.high - fb.low\n).plot(\n kind='scatter', x='volume', y='max_abs_change',\n title='Facebook Daily High - Low vs. Volume Traded'\n)\n\n\n\n\n\n\n\n\nThe relationship doesn’t seem to be linear, but we can try a log transform on the x-axis since the scales of the axes are very different. With pandas, we simply pass in logx=True:\n\nfb.assign(\n max_abs_change=fb.high - fb.low\n).plot(\n kind='scatter', x='volume', y='max_abs_change',\n title='Facebook Daily High - Low vs. log(Volume Traded)', \n logx=True\n)\n\n\n\n\n\n\n\n\nWith matplotlib, we could use plt.xscale('log') to do the same thing.\n\n\n\nSometimes our plots have many overlapping values, but this can be impossible to see. This can be addressed by increasing the transparency of what we are plotting using the alpha parameter. It is a float in the range [0, 1] where 0 is completely transparent and 1 is completely opaque. By default this is 1, so let’s put in a lower value and re-plot the scatter plot:\n\nfb.assign(\n max_abs_change=fb.high - fb.low\n).plot(\n kind='scatter', x='volume', y='max_abs_change',\n title='Facebook Daily High - Low vs. log(Volume Traded)',\n logx=True, alpha=0.25\n)\n\n\n\n\n\n\n\n\n\n\n\nIn the previous example, we can start to see the overlaps, but it is still difficult. Hexbins are another plot type that divide up the plot into hexagons, which are shaded according to the density of points there. With pandas, this is the hexbin value for the kind argument. It may also be necessary to tweak the gridsize, which determines the number of hexagons along the y-axis:\n\nfb.assign(\n log_volume=np.log(fb.volume),\n max_abs_change=fb.high - fb.low\n).plot(\n kind='hexbin',\n x='log_volume',\n y='max_abs_change',\n title='Facebook Daily High - Low vs. log(Volume Traded)',\n colormap='gray_r',\n gridsize=20, \n sharex=False # we have to pass this to see the x-axis\n)\n\n\n\n\n\n\n\n\n\n\n\nPandas doesn’t offer heatmaps; however, if we are able to get our data into a matrix, we can use matshow() from matplotlib:\n\nfig, ax = plt.subplots(figsize=(20, 10))\n\n# calculate the correlation matrix\nfb_corr = fb.assign(\n log_volume=np.log(fb.volume),\n max_abs_change=fb.high - fb.low\n).corr()\n\n# create the heatmap and colorbar\nim = ax.matshow(fb_corr, cmap='seismic')\nim.set_clim(-1, 1)\nfig.colorbar(im)\n\n# label the ticks with the column names\nlabels = [col.lower() for col in fb_corr.columns]\nax.set_xticks(ax.get_xticks()[1:-1]) # to handle bug in matplotlib\nax.set_xticklabels(labels, rotation=45)\nax.set_yticks(ax.get_yticks()[1:-1]) # to handle bug in matplotlib\nax.set_yticklabels(labels)\n\n# include the value of the correlation coefficient in the boxes\nfor (i, j), coef in np.ndenumerate(fb_corr):\n ax.text(\n i, j, fr'$\\rho$ = {coef:.2f}', # raw (r), format (f) string\n ha='center', va='center', \n color='white', fontsize=14\n )\n\n\n\n\n\n\n\n\nAccessing the values in the correlation matrix can be done with loc[]:\n\nfb_corr.loc['max_abs_change', ['volume', 'log_volume']]\n\nvolume 0.642027\nlog_volume 0.731542\nName: max_abs_change, dtype: float64", + "crumbs": [ + "Home", + "Plotting with Pandas" + ] + }, + { + "objectID": "notebooks/plotting_with_pandas.html#visualizing-distributions", + "href": "notebooks/plotting_with_pandas.html#visualizing-distributions", + "title": "Plotting with Pandas", + "section": "", + "text": "With the pandas, making histograms is as easy as passing kind='hist' to the plot() method:\n\nfb.volume.plot(\n kind='hist', \n title='Histogram of Daily Volume Traded in Facebook Stock'\n)\nplt.xlabel('Volume traded') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'Volume traded')\n\n\n\n\n\n\n\n\n\nWe can overlap histograms to compare distributions provided we use the alpha parameter. For example, let’s compare the usage and magnitude of the various measurement techniques (the magType column) in the data:\n\nfig, axes = plt.subplots(figsize=(8, 5))\n\nfor magtype in quakes.magType.unique():\n data = quakes.query(f'magType == \"{magtype}\"').mag\n if not data.empty:\n data.plot(\n kind='hist', ax=axes, alpha=0.4, \n label=magtype, legend=True,\n title='Comparing histograms of earthquake magnitude by magType'\n )\n\nplt.xlabel('magnitude') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'magnitude')\n\n\n\n\n\n\n\n\n\n\n\n\nWe can pass kind='kde' for an estimate of the probability density function (PDF), which tells us the probability of getting a particular value:\n\nfb.high.plot(\n kind='kde', \n title='KDE of Daily High Price for Facebook Stock'\n)\nplt.xlabel('Price ($)') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'Price ($)')\n\n\n\n\n\n\n\n\n\n\n\n\nThe plot() method returns an Axes object. We can store this for additional customization of the plot, or we can pass this into another call to plot() as the ax argument to add to the original plot.\nIt can often be helpful to view the KDE superimposed on top of the histogram, which can be achieved with this strategy:\n\nax = fb.high.plot(kind='hist', density=True, alpha=0.5)\nfb.high.plot(\n ax=ax, kind='kde', color='blue', \n title='Distribution of Facebook Stock\\'s Daily High Price in 2018'\n)\nplt.xlabel('Price ($)') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'Price ($)')\n\n\n\n\n\n\n\n\n\n\n\n\nIn some cases, we are more interested in the probability of getting less than or equal to that value (or greater than or equal), which we can see with the cumulative disribution function (CDF). Using the statsmodels package, we can estimate the CDF giving us the empirical cumulative distribution function (ECDF):\n\nfrom statsmodels.distributions.empirical_distribution import ECDF\n\necdf = ECDF(quakes.query('magType == \"ml\"').mag)\nplt.plot(ecdf.x, ecdf.y)\n\n# axis labels (we will cover this in chapter 6)\nplt.xlabel('mag') # add x-axis label \nplt.ylabel('cumulative probability') # add y-axis label\n\n# add title (we will cover this in chapter 6)\nplt.title('ECDF of earthquake magnitude with magType ml')\n\nText(0.5, 1.0, 'ECDF of earthquake magnitude with magType ml')\n\n\n\n\n\n\n\n\n\nThis ECDF tells us the probability of getting an earthquake with magnitude of 3 or less using the ml scale is 98%:\n\nfrom statsmodels.distributions.empirical_distribution import ECDF\n\necdf = ECDF(quakes.query('magType == \"ml\"').mag)\nplt.plot(ecdf.x, ecdf.y)\n\n# formatting below will all be covered in chapter 6\n# axis labels\nplt.xlabel('mag') # add x-axis label \nplt.ylabel('cumulative probability') # add y-axis label\n\n# add reference lines for interpreting the ECDF for mag <= 3 \nplt.plot(\n [3, 3], [0, .98], '--k', \n [-1.5, 3], [0.98, 0.98], '--k', alpha=0.4\n)\n\n# set axis ranges\nplt.ylim(0, None)\nplt.xlim(-1.25, None)\n\n# add a title\nplt.title('P(mag <= 3) = 98%')\n\nText(0.5, 1.0, 'P(mag <= 3) = 98%')\n\n\n\n\n\n\n\n\n\n\n\n\nTo make box plots with pandas, we pass kind='box' to the plot() method:\n\nfb.iloc[:,:4].plot(kind='box', title='Facebook OHLC Prices Box Plot')\nplt.ylabel('price ($)') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'price ($)')\n\n\n\n\n\n\n\n\n\nIf we pass in notch=True, we get a notched box plot. The notch represents a 95% confidence interval around the median, which can be helpful when comparing differences. For an introduction to interpreting a notched box plot, see this Google sites page and this Towards Data Science article.\n\nfb.iloc[:,:4].plot(kind='box', title='Facebook OHLC Prices Box Plot', notch=True)\nplt.ylabel('price ($)') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'price ($)')\n\n\n\n\n\n\n\n\n\nThis can also be combined with a call to groupby():\n\nfb.assign(\n volume_bin=pd.cut(fb.volume, 3, labels=['low', 'med', 'high'])\n).groupby('volume_bin').boxplot(\n column=['open', 'high', 'low', 'close'],\n layout=(1, 3), figsize=(12, 3)\n)\nplt.suptitle('Facebook OHLC Box Plots by Volume Traded', y=1.1)\n\nText(0.5, 1.1, 'Facebook OHLC Box Plots by Volume Traded')\n\n\n\n\n\n\n\n\n\nWe can use this to see the distribution of magnitudes across the different measurement methods for earthquakes:\n\nquakes[['mag', 'magType']].groupby('magType').boxplot(\n figsize=(15, 8), subplots=False\n)\nplt.title('Earthquake Magnitude Box Plots by magType')\nplt.ylabel('magnitude') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'magnitude')", + "crumbs": [ + "Home", + "Plotting with Pandas" + ] + }, + { + "objectID": "notebooks/plotting_with_pandas.html#counts-and-frequencies", + "href": "notebooks/plotting_with_pandas.html#counts-and-frequencies", + "title": "Plotting with Pandas", + "section": "", + "text": "Passing kind='barh' gives us horizontal bars while kind='bar' gives us vertical ones. Let’s use horizontal bars to look at the top 15 places for earthquakes in our data:\n\nquakes.parsed_place.value_counts().iloc[14::-1,].plot(\n kind='barh', figsize=(10, 5),\n title='Top 15 Places for Earthquakes '\n '(September 18, 2018 - October 13, 2018)'\n)\nplt.xlabel('earthquakes') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'earthquakes')\n\n\n\n\n\n\n\n\n\nWe also have data on whether earthquakes were accompanied by tsunamis. Let’s see what the top places for tsunamis are:\n\nquakes.groupby('parsed_place').tsunami.sum().sort_values().iloc[-10:,].plot(\n kind='barh', figsize=(10, 5), \n title='Top 10 Places for Tsunamis '\n '(September 18, 2018 - October 13, 2018)'\n)\nplt.xlabel('tsunamis') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'tsunamis')\n\n\n\n\n\n\n\n\n\nSeeing that Indonesia is the top place for tsunamis during the time period we are looking at, we may want to look how many earthquakes and tsunamis Indonesia gets on a daily basis. We could show this as a line plot or with bars; since we don’t want to interpolate, we will use bars here:\n\nindonesia_quakes = quakes.query('parsed_place == \"Indonesia\"').assign(\n time=lambda x: pd.to_datetime(x.time, unit='ms'),\n earthquake=1\n).set_index('time').resample('1D').sum()\n\n# format the datetimes in the index for the x-axis\nindonesia_quakes.index = indonesia_quakes.index.strftime('%b\\n%d')\n\nindonesia_quakes.plot(\n y=['earthquake', 'tsunami'], kind='bar', figsize=(15, 3), \n rot=0, label=['earthquakes', 'tsunamis'], \n title='Earthquakes and Tsunamis in Indonesia '\n '(September 18, 2018 - October 13, 2018)'\n)\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('date')\nplt.ylabel('count')\n\nC:\\Users\\gpower\\AppData\\Local\\Temp\\ipykernel_13112\\3940988219.py:4: FutureWarning: The default value of numeric_only in DataFrameGroupBy.sum is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.\n ).set_index('time').resample('1D').sum()\n\n\nText(0, 0.5, 'count')\n\n\n\n\n\n\n\n\n\n\n\n\n\nquakes.groupby(['parsed_place', 'tsunami']).mag.count()\\\n .unstack().apply(lambda x: x / x.sum(), axis=1)\\\n .rename(columns={0: 'no', 1: 'yes'})\\\n .sort_values('yes', ascending=False)[7::-1]\\\n .plot.barh(\n title='Frequency of a tsunami accompanying an earthquake'\n )\n\n# move legend to the right of the plot\nplt.legend(title='tsunami?', bbox_to_anchor=(1, 0.65))\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('percentage of earthquakes')\nplt.ylabel('')\n\nText(0, 0.5, '')\n\n\n\n\n\n\n\n\n\nUsing the kind arugment for vertical bars when the labels for each bar are shorter:\n\nquakes.magType.value_counts().plot(\n kind='bar', title='Earthquakes Recorded per magType', rot=0\n)\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('magType')\nplt.ylabel('earthquakes')\n\nText(0, 0.5, 'earthquakes')\n\n\n\n\n\n\n\n\n\n\n\n\n\npivot = quakes.assign(\n mag_bin=lambda x: np.floor(x.mag)\n).pivot_table(\n index='mag_bin', columns='magType', values='mag', aggfunc='count'\n)\npivot.plot.bar(\n stacked=True, rot=0, ylabel='earthquakes', \n title='Earthquakes by integer magnitude and magType'\n)\n\n\n\n\n\n\n\n\n\n\nPlot the percentages to be better able to see the different magTypes.\n\nnormalized_pivot = pivot.fillna(0).apply(lambda x: x / x.sum(), axis=1)\nax = normalized_pivot.plot.bar(\n stacked=True, rot=0, figsize=(10, 5),\n title='Percentage of earthquakes by integer magnitude for each magType'\n)\nax.legend(bbox_to_anchor=(1, 0.8)) # move legend to the right of the plot\nplt.ylabel('percentage') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'percentage')\n\n\n\n\n\n\n\n\n\nWe can also create horizontal stacked bars and do so using groupby() and unstack():\n\nquakes.groupby(['parsed_place', 'tsunami']).mag.count()\\\n .unstack().apply(lambda x: x / x.sum(), axis=1)\\\n .rename(columns={0: 'no', 1: 'yes'})\\\n .sort_values('yes', ascending=False)[7::-1]\\\n .plot.barh(\n title='Frequency of a tsunami accompanying an earthquake', \n stacked=True\n )\n\n# move legend to the right of the plot\nplt.legend(title='tsunami?', bbox_to_anchor=(1, 0.65))\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('percentage of earthquakes')\nplt.ylabel('')\n\nText(0, 0.5, '')", + "crumbs": [ + "Home", + "Plotting with Pandas" + ] + }, + { + "objectID": "notebooks/python_errors.html", + "href": "notebooks/python_errors.html", + "title": "Python Errors", + "section": "", + "text": "When an error arises, there will be an error message with the type of error and the line the error occured on. This notebook goes over how to handle the common types of errors and exceptions in Python.\nI recommend looking at the Python tutorial page for more information on errors. Searching for the error message directly on Google can help the debugging process if there is an error not discussed in this page.\n\n\nA SyntaxError occurs when the syntax of your code is incorrect.\n\nif True \nprint(\"Hello World\")\n\nSyntaxError: expected ':' (975521850.py, line 1)\n\n\nA colon is expected after the if statement, which arises the syntax error. The error goes away after adding the colon.\n\nif True:\n print(\"Hello World\")\n\nHello World\n\n\n\n\n\nA NameError occurs when a variable, function, or module used does not exist. When this happens, it is usually because of a spelling error.\n\nadd\n\nNameError: name 'add' is not defined\n\n\n\nstring(9)\n\nNameError: name 'string' is not defined\n\n\n\n\n\nA TypeError occurs when you input an incorrect data type for an operation or function.\n\n\"abc\" + 9\n\nTypeError: can only concatenate str (not \"int\") to str\n\n\nIn python, you cannot add strings to integers. You can add, however, an integer to an integer or a string to a string with a +.\n\n9 + 9\n\n18\n\n\n\n\"abc\" + \"def\"\n\n'abcdef'\n\n\n\n\n\nA ZeroDivisionError occurs when you try to divide by zero. To fix this, recheck your computation.\n\n2 / (9 * 0)\n\nZeroDivisionError: division by zero\n\n\n\n#code corrected to no longer divide by zero\n(2 / 9) * 0\n\n0.0\n\n\n\n\n\nA ValueError occurs when an input for a function is the correct data type but is invalid in regards to the domain of the function. This is most common with mathematical operations.\n\nimport math\n\nmath.sqrt(-10)\n\nValueError: math domain error\n\n\nIn the example above, you must input a positive number into the sqrt() function. The negative number is still an integer, but it is not in the function’s domain.\n\nmath.sqrt(10)\n\n3.1622776601683795\n\n\n\n\n\nAn IndexError occurs when you try to access an item in a list with an index out of bounds.\n\nlist = [1,2,3,4,5]\nlist[5]\n\nIndexError: list index out of range\n\n\nThe range of a list is [0, n-1], where “n” is the length of the list. So, the list [1,2,3,4,5] has index elements in the range 0-4.\n\nlist[4]\n\n5\n\n\n\n\n\nA ModuleNotFoundError occurs when you try to import a module that does not exist. It is a type of ImportError. To fix this error, check if you have installed the module in your python environment from the terminal command-line.\n\nimport pillow\n\nModuleNotFoundError: No module named 'pillow'\n\n\n\n\n\nYou can use a try statement to catch errors. A try clause includes the code you want to run that might cause an error. If no error occurs, the try clause runs successfully. If an error does occur, the except clause runs after the line in the try clause that caused an error.\n\ntry:\n \"abc\" + 9\n print(\"Success\")\nexcept:\n print(\"Failure to execute\")\n\nFailure to execute\n\n\nThe except clause above can catch any type of error. However, an except clause can also catch a specific type of error. There can be mulptile except clauses in a try statement to catch the different types of errors.\n\ntry:\n hello\n \"abc\" + 9\n print(\"Success\")\nexcept TypeError:\n print(\"TypeError failure to execute\")\nexcept NameError:\n print(\"NameError failure to execute\")\n\nNameError failure to execute\n\n\n\ntry:\n list = [1,2,3,4,5]\n list[5]\n print(\"Success\")\nexcept TypeError:\n print(\"TypeError failure to execute\")\nexcept NameError:\n print(\"NameError failure to execute\")\nexcept IndexError:\n print(\"IndexError failure to execute\")\n\nIndexError failure to execute\n\n\n\n\n\nThese are not all the errors that might come up in your coding. If another type of error occurs, you can search the error type on Google to learn more about what has caused it. As always, remember to look at the line resulting in the error for hints on what could have gone wrong!", + "crumbs": [ + "Home", + "Python Errors" + ] + }, + { + "objectID": "notebooks/python_errors.html#syntax-error", + "href": "notebooks/python_errors.html#syntax-error", + "title": "Python Errors", + "section": "", + "text": "A SyntaxError occurs when the syntax of your code is incorrect.\n\nif True \nprint(\"Hello World\")\n\nSyntaxError: expected ':' (975521850.py, line 1)\n\n\nA colon is expected after the if statement, which arises the syntax error. The error goes away after adding the colon.\n\nif True:\n print(\"Hello World\")\n\nHello World", + "crumbs": [ + "Home", + "Python Errors" + ] + }, + { + "objectID": "notebooks/python_errors.html#name-error", + "href": "notebooks/python_errors.html#name-error", + "title": "Python Errors", + "section": "", + "text": "A NameError occurs when a variable, function, or module used does not exist. When this happens, it is usually because of a spelling error.\n\nadd\n\nNameError: name 'add' is not defined\n\n\n\nstring(9)\n\nNameError: name 'string' is not defined", + "crumbs": [ + "Home", + "Python Errors" + ] + }, + { + "objectID": "notebooks/python_errors.html#type-error", + "href": "notebooks/python_errors.html#type-error", + "title": "Python Errors", + "section": "", + "text": "A TypeError occurs when you input an incorrect data type for an operation or function.\n\n\"abc\" + 9\n\nTypeError: can only concatenate str (not \"int\") to str\n\n\nIn python, you cannot add strings to integers. You can add, however, an integer to an integer or a string to a string with a +.\n\n9 + 9\n\n18\n\n\n\n\"abc\" + \"def\"\n\n'abcdef'", + "crumbs": [ + "Home", + "Python Errors" + ] + }, + { + "objectID": "notebooks/python_errors.html#zero-division-error", + "href": "notebooks/python_errors.html#zero-division-error", + "title": "Python Errors", + "section": "", + "text": "A ZeroDivisionError occurs when you try to divide by zero. To fix this, recheck your computation.\n\n2 / (9 * 0)\n\nZeroDivisionError: division by zero\n\n\n\n#code corrected to no longer divide by zero\n(2 / 9) * 0\n\n0.0", + "crumbs": [ + "Home", + "Python Errors" + ] + }, + { + "objectID": "notebooks/python_errors.html#value-error", + "href": "notebooks/python_errors.html#value-error", + "title": "Python Errors", + "section": "", + "text": "A ValueError occurs when an input for a function is the correct data type but is invalid in regards to the domain of the function. This is most common with mathematical operations.\n\nimport math\n\nmath.sqrt(-10)\n\nValueError: math domain error\n\n\nIn the example above, you must input a positive number into the sqrt() function. The negative number is still an integer, but it is not in the function’s domain.\n\nmath.sqrt(10)\n\n3.1622776601683795", + "crumbs": [ + "Home", + "Python Errors" + ] + }, + { + "objectID": "notebooks/python_errors.html#index-error", + "href": "notebooks/python_errors.html#index-error", + "title": "Python Errors", + "section": "", + "text": "An IndexError occurs when you try to access an item in a list with an index out of bounds.\n\nlist = [1,2,3,4,5]\nlist[5]\n\nIndexError: list index out of range\n\n\nThe range of a list is [0, n-1], where “n” is the length of the list. So, the list [1,2,3,4,5] has index elements in the range 0-4.\n\nlist[4]\n\n5", + "crumbs": [ + "Home", + "Python Errors" + ] + }, + { + "objectID": "notebooks/python_errors.html#module-not-found-error", + "href": "notebooks/python_errors.html#module-not-found-error", + "title": "Python Errors", + "section": "", + "text": "A ModuleNotFoundError occurs when you try to import a module that does not exist. It is a type of ImportError. To fix this error, check if you have installed the module in your python environment from the terminal command-line.\n\nimport pillow\n\nModuleNotFoundError: No module named 'pillow'", + "crumbs": [ + "Home", + "Python Errors" + ] + }, + { + "objectID": "notebooks/python_errors.html#catching-exceptions-with-try-statements", + "href": "notebooks/python_errors.html#catching-exceptions-with-try-statements", + "title": "Python Errors", + "section": "", + "text": "You can use a try statement to catch errors. A try clause includes the code you want to run that might cause an error. If no error occurs, the try clause runs successfully. If an error does occur, the except clause runs after the line in the try clause that caused an error.\n\ntry:\n \"abc\" + 9\n print(\"Success\")\nexcept:\n print(\"Failure to execute\")\n\nFailure to execute\n\n\nThe except clause above can catch any type of error. However, an except clause can also catch a specific type of error. There can be mulptile except clauses in a try statement to catch the different types of errors.\n\ntry:\n hello\n \"abc\" + 9\n print(\"Success\")\nexcept TypeError:\n print(\"TypeError failure to execute\")\nexcept NameError:\n print(\"NameError failure to execute\")\n\nNameError failure to execute\n\n\n\ntry:\n list = [1,2,3,4,5]\n list[5]\n print(\"Success\")\nexcept TypeError:\n print(\"TypeError failure to execute\")\nexcept NameError:\n print(\"NameError failure to execute\")\nexcept IndexError:\n print(\"IndexError failure to execute\")\n\nIndexError failure to execute", + "crumbs": [ + "Home", + "Python Errors" + ] + }, + { + "objectID": "notebooks/python_errors.html#next-steps", + "href": "notebooks/python_errors.html#next-steps", + "title": "Python Errors", + "section": "", + "text": "These are not all the errors that might come up in your coding. If another type of error occurs, you can search the error type on Google to learn more about what has caused it. As always, remember to look at the line resulting in the error for hints on what could have gone wrong!", + "crumbs": [ + "Home", + "Python Errors" + ] + }, + { + "objectID": "notebooks/wide_vs_long.html", + "href": "notebooks/wide_vs_long.html", + "title": "Wide vs. Long Format Data", + "section": "", + "text": "In this notebook, we will be using daily temperature data from the National Centers for Environmental Information (NCEI) API. We will use the Global Historical Climatology Network - Daily (GHCND) dataset for the Boonton 1 station (GHCND:USC00280907); see the documentation here.\nNote: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.\n\n\n\n\nimport matplotlib.pyplot as plt\nimport pandas as pd\n\nwide_df = pd.read_csv('../data/wide_data.csv', parse_dates=['date'])\nlong_df = pd.read_csv(\n '../data/long_data.csv', \n usecols=['date', 'datatype', 'value'], \n parse_dates=['date']\n)[['date', 'datatype', 'value']] # sort columns\n\n\n\n\nOur variables each have their own column:\n\nwide_df.head(6)\n\n\n\n\n\n\n\n\ndate\nTMAX\nTMIN\nTOBS\n\n\n\n\n0\n2018-10-01\n21.1\n8.9\n13.9\n\n\n1\n2018-10-02\n23.9\n13.9\n17.2\n\n\n2\n2018-10-03\n25.0\n15.6\n16.1\n\n\n3\n2018-10-04\n22.8\n11.7\n11.7\n\n\n4\n2018-10-05\n23.3\n11.7\n18.9\n\n\n5\n2018-10-06\n20.0\n13.3\n16.1\n\n\n\n\n\n\n\nDescribing all the columns is easy:\n\nwide_df.describe(include='all')\n\n\n\n\n\n\n\n\ndate\nTMAX\nTMIN\nTOBS\n\n\n\n\ncount\n31\n31.000000\n31.000000\n31.000000\n\n\nmean\n2018-10-16 00:00:00\n16.829032\n7.561290\n10.022581\n\n\nmin\n2018-10-01 00:00:00\n7.800000\n-1.100000\n-1.100000\n\n\n25%\n2018-10-08 12:00:00\n12.750000\n2.500000\n5.550000\n\n\n50%\n2018-10-16 00:00:00\n16.100000\n6.700000\n8.300000\n\n\n75%\n2018-10-23 12:00:00\n21.950000\n13.600000\n16.100000\n\n\nmax\n2018-10-31 00:00:00\n26.700000\n17.800000\n21.700000\n\n\nstd\nNaN\n5.714962\n6.513252\n6.596550\n\n\n\n\n\n\n\nIt’s easy to graph with pandas:\n\nwide_df.plot(\n x='date', y=['TMAX', 'TMIN', 'TOBS'], figsize=(15, 5), \n title='Temperature in NYC in October 2018'\n).set_ylabel('Temperature in Celsius')\nplt.show()\n\n\n\n\n\n\n\n\n\n\n\nOur variable names are now in the datatype column and their values are in the value column. We now have 3 rows for each date, since we have 3 different datatypes:\n\nlong_df.head(6)\n\n\n\n\n\n\n\n\ndate\ndatatype\nvalue\n\n\n\n\n0\n2018-10-01\nTMAX\n21.1\n\n\n1\n2018-10-01\nTMIN\n8.9\n\n\n2\n2018-10-01\nTOBS\n13.9\n\n\n3\n2018-10-02\nTMAX\n23.9\n\n\n4\n2018-10-02\nTMIN\n13.9\n\n\n5\n2018-10-02\nTOBS\n17.2\n\n\n\n\n\n\n\nSince we have many rows for the same date, using describe() is not that helpful:\n\nlong_df.describe(include='all')\n\n\n\n\n\n\n\n\ndate\ndatatype\nvalue\n\n\n\n\ncount\n93\n93\n93.000000\n\n\nunique\nNaN\n3\nNaN\n\n\ntop\nNaN\nTMAX\nNaN\n\n\nfreq\nNaN\n31\nNaN\n\n\nmean\n2018-10-16 00:00:00\nNaN\n11.470968\n\n\nmin\n2018-10-01 00:00:00\nNaN\n-1.100000\n\n\n25%\n2018-10-08 00:00:00\nNaN\n6.700000\n\n\n50%\n2018-10-16 00:00:00\nNaN\n11.700000\n\n\n75%\n2018-10-24 00:00:00\nNaN\n17.200000\n\n\nmax\n2018-10-31 00:00:00\nNaN\n26.700000\n\n\nstd\nNaN\nNaN\n7.362354\n\n\n\n\n\n\n\nPlotting long format data in pandas can be rather tricky. Instead we use seaborn:\n\nimport seaborn as sns\n\nsns.set(rc={'figure.figsize': (15, 5)}, style='white')\n\nax = sns.lineplot(\n data=long_df, x='date', y='value', hue='datatype'\n)\nax.set_ylabel('Temperature in Celsius')\nax.set_title('Temperature in NYC in October 2018')\nplt.show()\n\n\n\n\n\n\n\n\nWith long data and seaborn, we can easily facet our plots:\n\nsns.set(\n rc={'figure.figsize': (20, 10)}, style='white', font_scale=2\n)\n\ng = sns.FacetGrid(long_df, col='datatype', height=10)\ng = g.map(plt.plot, 'date', 'value')\ng.set_titles(size=25)\ng.set_xticklabels(rotation=45)\nplt.show()", + "crumbs": [ + "Home", + "Wide vs. Long Format Data" + ] + }, + { + "objectID": "notebooks/wide_vs_long.html#about-the-data", + "href": "notebooks/wide_vs_long.html#about-the-data", + "title": "Wide vs. Long Format Data", + "section": "", + "text": "In this notebook, we will be using daily temperature data from the National Centers for Environmental Information (NCEI) API. We will use the Global Historical Climatology Network - Daily (GHCND) dataset for the Boonton 1 station (GHCND:USC00280907); see the documentation here.\nNote: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.", + "crumbs": [ + "Home", + "Wide vs. Long Format Data" + ] + }, + { + "objectID": "notebooks/wide_vs_long.html#setup", + "href": "notebooks/wide_vs_long.html#setup", + "title": "Wide vs. Long Format Data", + "section": "", + "text": "import matplotlib.pyplot as plt\nimport pandas as pd\n\nwide_df = pd.read_csv('../data/wide_data.csv', parse_dates=['date'])\nlong_df = pd.read_csv(\n '../data/long_data.csv', \n usecols=['date', 'datatype', 'value'], \n parse_dates=['date']\n)[['date', 'datatype', 'value']] # sort columns", + "crumbs": [ + "Home", + "Wide vs. Long Format Data" + ] + }, + { + "objectID": "notebooks/wide_vs_long.html#wide-format", + "href": "notebooks/wide_vs_long.html#wide-format", + "title": "Wide vs. Long Format Data", + "section": "", + "text": "Our variables each have their own column:\n\nwide_df.head(6)\n\n\n\n\n\n\n\n\ndate\nTMAX\nTMIN\nTOBS\n\n\n\n\n0\n2018-10-01\n21.1\n8.9\n13.9\n\n\n1\n2018-10-02\n23.9\n13.9\n17.2\n\n\n2\n2018-10-03\n25.0\n15.6\n16.1\n\n\n3\n2018-10-04\n22.8\n11.7\n11.7\n\n\n4\n2018-10-05\n23.3\n11.7\n18.9\n\n\n5\n2018-10-06\n20.0\n13.3\n16.1\n\n\n\n\n\n\n\nDescribing all the columns is easy:\n\nwide_df.describe(include='all')\n\n\n\n\n\n\n\n\ndate\nTMAX\nTMIN\nTOBS\n\n\n\n\ncount\n31\n31.000000\n31.000000\n31.000000\n\n\nmean\n2018-10-16 00:00:00\n16.829032\n7.561290\n10.022581\n\n\nmin\n2018-10-01 00:00:00\n7.800000\n-1.100000\n-1.100000\n\n\n25%\n2018-10-08 12:00:00\n12.750000\n2.500000\n5.550000\n\n\n50%\n2018-10-16 00:00:00\n16.100000\n6.700000\n8.300000\n\n\n75%\n2018-10-23 12:00:00\n21.950000\n13.600000\n16.100000\n\n\nmax\n2018-10-31 00:00:00\n26.700000\n17.800000\n21.700000\n\n\nstd\nNaN\n5.714962\n6.513252\n6.596550\n\n\n\n\n\n\n\nIt’s easy to graph with pandas:\n\nwide_df.plot(\n x='date', y=['TMAX', 'TMIN', 'TOBS'], figsize=(15, 5), \n title='Temperature in NYC in October 2018'\n).set_ylabel('Temperature in Celsius')\nplt.show()", + "crumbs": [ + "Home", + "Wide vs. Long Format Data" + ] + }, + { + "objectID": "notebooks/wide_vs_long.html#long-format", + "href": "notebooks/wide_vs_long.html#long-format", + "title": "Wide vs. Long Format Data", + "section": "", + "text": "Our variable names are now in the datatype column and their values are in the value column. We now have 3 rows for each date, since we have 3 different datatypes:\n\nlong_df.head(6)\n\n\n\n\n\n\n\n\ndate\ndatatype\nvalue\n\n\n\n\n0\n2018-10-01\nTMAX\n21.1\n\n\n1\n2018-10-01\nTMIN\n8.9\n\n\n2\n2018-10-01\nTOBS\n13.9\n\n\n3\n2018-10-02\nTMAX\n23.9\n\n\n4\n2018-10-02\nTMIN\n13.9\n\n\n5\n2018-10-02\nTOBS\n17.2\n\n\n\n\n\n\n\nSince we have many rows for the same date, using describe() is not that helpful:\n\nlong_df.describe(include='all')\n\n\n\n\n\n\n\n\ndate\ndatatype\nvalue\n\n\n\n\ncount\n93\n93\n93.000000\n\n\nunique\nNaN\n3\nNaN\n\n\ntop\nNaN\nTMAX\nNaN\n\n\nfreq\nNaN\n31\nNaN\n\n\nmean\n2018-10-16 00:00:00\nNaN\n11.470968\n\n\nmin\n2018-10-01 00:00:00\nNaN\n-1.100000\n\n\n25%\n2018-10-08 00:00:00\nNaN\n6.700000\n\n\n50%\n2018-10-16 00:00:00\nNaN\n11.700000\n\n\n75%\n2018-10-24 00:00:00\nNaN\n17.200000\n\n\nmax\n2018-10-31 00:00:00\nNaN\n26.700000\n\n\nstd\nNaN\nNaN\n7.362354\n\n\n\n\n\n\n\nPlotting long format data in pandas can be rather tricky. Instead we use seaborn:\n\nimport seaborn as sns\n\nsns.set(rc={'figure.figsize': (15, 5)}, style='white')\n\nax = sns.lineplot(\n data=long_df, x='date', y='value', hue='datatype'\n)\nax.set_ylabel('Temperature in Celsius')\nax.set_title('Temperature in NYC in October 2018')\nplt.show()\n\n\n\n\n\n\n\n\nWith long data and seaborn, we can easily facet our plots:\n\nsns.set(\n rc={'figure.figsize': (20, 10)}, style='white', font_scale=2\n)\n\ng = sns.FacetGrid(long_df, col='datatype', height=10)\ng = g.map(plt.plot, 'date', 'value')\ng.set_titles(size=25)\ng.set_xticklabels(rotation=45)\nplt.show()", + "crumbs": [ + "Home", + "Wide vs. Long Format Data" + ] + } +] \ No newline at end of file diff --git a/_site/site_libs/bootstrap/bootstrap-icons.css b/_site/site_libs/bootstrap/bootstrap-icons.css new file mode 100644 index 0000000..285e444 --- /dev/null +++ b/_site/site_libs/bootstrap/bootstrap-icons.css @@ -0,0 +1,2078 @@ +/*! + * Bootstrap Icons v1.11.1 (https://icons.getbootstrap.com/) + * Copyright 2019-2023 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE) + */ + +@font-face { + font-display: block; + font-family: "bootstrap-icons"; + src: +url("./bootstrap-icons.woff?2820a3852bdb9a5832199cc61cec4e65") 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-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-fill::before { content: "\f68b"; } +.bi-envelope-check::before { content: "\f68c"; } +.bi-envelope-dash-fill::before { content: "\f68e"; } +.bi-envelope-dash::before { content: "\f68f"; } +.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-fill::before { content: "\f696"; } +.bi-envelope-slash::before { content: "\f697"; } +.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-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-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::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::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-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::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::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"; } +.bi-1-circle-fill::before { content: "\f796"; } +.bi-1-circle::before { content: "\f797"; } +.bi-1-square-fill::before { content: "\f798"; } +.bi-1-square::before { content: "\f799"; } +.bi-2-circle-fill::before { content: "\f79c"; } +.bi-2-circle::before { content: "\f79d"; } +.bi-2-square-fill::before { content: "\f79e"; } +.bi-2-square::before { content: "\f79f"; } +.bi-3-circle-fill::before { content: "\f7a2"; } +.bi-3-circle::before { content: "\f7a3"; } +.bi-3-square-fill::before { content: "\f7a4"; } +.bi-3-square::before { content: "\f7a5"; } +.bi-4-circle-fill::before { content: "\f7a8"; } +.bi-4-circle::before { content: "\f7a9"; } +.bi-4-square-fill::before { content: "\f7aa"; } +.bi-4-square::before { content: "\f7ab"; } +.bi-5-circle-fill::before { content: "\f7ae"; } +.bi-5-circle::before { content: "\f7af"; } +.bi-5-square-fill::before { content: "\f7b0"; } +.bi-5-square::before { content: "\f7b1"; } +.bi-6-circle-fill::before { content: "\f7b4"; } +.bi-6-circle::before { content: "\f7b5"; } +.bi-6-square-fill::before { content: "\f7b6"; } +.bi-6-square::before { content: "\f7b7"; } +.bi-7-circle-fill::before { content: "\f7ba"; } +.bi-7-circle::before { content: "\f7bb"; } +.bi-7-square-fill::before { content: "\f7bc"; } +.bi-7-square::before { content: "\f7bd"; } +.bi-8-circle-fill::before { content: "\f7c0"; } +.bi-8-circle::before { content: "\f7c1"; } +.bi-8-square-fill::before { content: "\f7c2"; } +.bi-8-square::before { content: "\f7c3"; } +.bi-9-circle-fill::before { content: "\f7c6"; } +.bi-9-circle::before { content: "\f7c7"; } +.bi-9-square-fill::before { content: "\f7c8"; } +.bi-9-square::before { content: "\f7c9"; } +.bi-airplane-engines-fill::before { content: "\f7ca"; } +.bi-airplane-engines::before { content: "\f7cb"; } +.bi-airplane-fill::before { content: "\f7cc"; } +.bi-airplane::before { content: "\f7cd"; } +.bi-alexa::before { content: "\f7ce"; } +.bi-alipay::before { content: "\f7cf"; } +.bi-android::before { content: "\f7d0"; } +.bi-android2::before { content: "\f7d1"; } +.bi-box-fill::before { content: "\f7d2"; } +.bi-box-seam-fill::before { content: "\f7d3"; } +.bi-browser-chrome::before { content: "\f7d4"; } +.bi-browser-edge::before { content: "\f7d5"; } +.bi-browser-firefox::before { content: "\f7d6"; } +.bi-browser-safari::before { content: "\f7d7"; } +.bi-c-circle-fill::before { content: "\f7da"; } +.bi-c-circle::before { content: "\f7db"; } +.bi-c-square-fill::before { content: "\f7dc"; } +.bi-c-square::before { content: "\f7dd"; } +.bi-capsule-pill::before { content: "\f7de"; } +.bi-capsule::before { content: "\f7df"; } +.bi-car-front-fill::before { content: "\f7e0"; } +.bi-car-front::before { content: "\f7e1"; } +.bi-cassette-fill::before { content: "\f7e2"; } +.bi-cassette::before { content: "\f7e3"; } +.bi-cc-circle-fill::before { content: "\f7e6"; } +.bi-cc-circle::before { content: "\f7e7"; } +.bi-cc-square-fill::before { content: "\f7e8"; } +.bi-cc-square::before { content: "\f7e9"; } +.bi-cup-hot-fill::before { content: "\f7ea"; } +.bi-cup-hot::before { content: "\f7eb"; } +.bi-currency-rupee::before { content: "\f7ec"; } +.bi-dropbox::before { content: "\f7ed"; } +.bi-escape::before { content: "\f7ee"; } +.bi-fast-forward-btn-fill::before { content: "\f7ef"; } +.bi-fast-forward-btn::before { content: "\f7f0"; } +.bi-fast-forward-circle-fill::before { content: "\f7f1"; } +.bi-fast-forward-circle::before { content: "\f7f2"; } +.bi-fast-forward-fill::before { content: "\f7f3"; } +.bi-fast-forward::before { content: "\f7f4"; } +.bi-filetype-sql::before { content: "\f7f5"; } +.bi-fire::before { content: "\f7f6"; } +.bi-google-play::before { content: "\f7f7"; } +.bi-h-circle-fill::before { content: "\f7fa"; } +.bi-h-circle::before { content: "\f7fb"; } +.bi-h-square-fill::before { content: "\f7fc"; } +.bi-h-square::before { content: "\f7fd"; } +.bi-indent::before { content: "\f7fe"; } +.bi-lungs-fill::before { content: "\f7ff"; } +.bi-lungs::before { content: "\f800"; } +.bi-microsoft-teams::before { content: "\f801"; } +.bi-p-circle-fill::before { content: "\f804"; } +.bi-p-circle::before { content: "\f805"; } +.bi-p-square-fill::before { content: "\f806"; } +.bi-p-square::before { content: "\f807"; } +.bi-pass-fill::before { content: "\f808"; } +.bi-pass::before { content: "\f809"; } +.bi-prescription::before { content: "\f80a"; } +.bi-prescription2::before { content: "\f80b"; } +.bi-r-circle-fill::before { content: "\f80e"; } +.bi-r-circle::before { content: "\f80f"; } +.bi-r-square-fill::before { content: "\f810"; } +.bi-r-square::before { content: "\f811"; } +.bi-repeat-1::before { content: "\f812"; } +.bi-repeat::before { content: "\f813"; } +.bi-rewind-btn-fill::before { content: "\f814"; } +.bi-rewind-btn::before { content: "\f815"; } +.bi-rewind-circle-fill::before { content: "\f816"; } +.bi-rewind-circle::before { content: "\f817"; } +.bi-rewind-fill::before { content: "\f818"; } +.bi-rewind::before { content: "\f819"; } +.bi-train-freight-front-fill::before { content: "\f81a"; } +.bi-train-freight-front::before { content: "\f81b"; } +.bi-train-front-fill::before { content: "\f81c"; } +.bi-train-front::before { content: "\f81d"; } +.bi-train-lightrail-front-fill::before { content: "\f81e"; } +.bi-train-lightrail-front::before { content: "\f81f"; } +.bi-truck-front-fill::before { content: "\f820"; } +.bi-truck-front::before { content: "\f821"; } +.bi-ubuntu::before { content: "\f822"; } +.bi-unindent::before { content: "\f823"; } +.bi-unity::before { content: "\f824"; } +.bi-universal-access-circle::before { content: "\f825"; } +.bi-universal-access::before { content: "\f826"; } +.bi-virus::before { content: "\f827"; } +.bi-virus2::before { content: "\f828"; } +.bi-wechat::before { content: "\f829"; } +.bi-yelp::before { content: "\f82a"; } +.bi-sign-stop-fill::before { content: "\f82b"; } +.bi-sign-stop-lights-fill::before { content: "\f82c"; } +.bi-sign-stop-lights::before { content: "\f82d"; } +.bi-sign-stop::before { content: "\f82e"; } +.bi-sign-turn-left-fill::before { content: "\f82f"; } +.bi-sign-turn-left::before { content: "\f830"; } +.bi-sign-turn-right-fill::before { content: "\f831"; } +.bi-sign-turn-right::before { content: "\f832"; } +.bi-sign-turn-slight-left-fill::before { content: "\f833"; } +.bi-sign-turn-slight-left::before { content: "\f834"; } +.bi-sign-turn-slight-right-fill::before { content: "\f835"; } +.bi-sign-turn-slight-right::before { content: "\f836"; } +.bi-sign-yield-fill::before { content: "\f837"; } +.bi-sign-yield::before { content: "\f838"; } +.bi-ev-station-fill::before { content: "\f839"; } +.bi-ev-station::before { content: "\f83a"; } +.bi-fuel-pump-diesel-fill::before { content: "\f83b"; } +.bi-fuel-pump-diesel::before { content: "\f83c"; } +.bi-fuel-pump-fill::before { content: "\f83d"; } +.bi-fuel-pump::before { content: "\f83e"; } +.bi-0-circle-fill::before { content: "\f83f"; } +.bi-0-circle::before { content: "\f840"; } +.bi-0-square-fill::before { content: "\f841"; } +.bi-0-square::before { content: "\f842"; } +.bi-rocket-fill::before { content: "\f843"; } +.bi-rocket-takeoff-fill::before { content: "\f844"; } +.bi-rocket-takeoff::before { content: "\f845"; } +.bi-rocket::before { content: "\f846"; } +.bi-stripe::before { content: "\f847"; } +.bi-subscript::before { content: "\f848"; } +.bi-superscript::before { content: "\f849"; } +.bi-trello::before { content: "\f84a"; } +.bi-envelope-at-fill::before { content: "\f84b"; } +.bi-envelope-at::before { content: "\f84c"; } +.bi-regex::before { content: "\f84d"; } +.bi-text-wrap::before { content: "\f84e"; } +.bi-sign-dead-end-fill::before { content: "\f84f"; } +.bi-sign-dead-end::before { content: "\f850"; } +.bi-sign-do-not-enter-fill::before { content: "\f851"; } +.bi-sign-do-not-enter::before { content: "\f852"; } +.bi-sign-intersection-fill::before { content: "\f853"; } +.bi-sign-intersection-side-fill::before { content: "\f854"; } +.bi-sign-intersection-side::before { content: "\f855"; } +.bi-sign-intersection-t-fill::before { content: "\f856"; } +.bi-sign-intersection-t::before { content: "\f857"; } +.bi-sign-intersection-y-fill::before { content: "\f858"; } +.bi-sign-intersection-y::before { content: "\f859"; } +.bi-sign-intersection::before { content: "\f85a"; } +.bi-sign-merge-left-fill::before { content: "\f85b"; } +.bi-sign-merge-left::before { content: "\f85c"; } +.bi-sign-merge-right-fill::before { content: "\f85d"; } +.bi-sign-merge-right::before { content: "\f85e"; } +.bi-sign-no-left-turn-fill::before { content: "\f85f"; } +.bi-sign-no-left-turn::before { content: "\f860"; } +.bi-sign-no-parking-fill::before { content: "\f861"; } +.bi-sign-no-parking::before { content: "\f862"; } +.bi-sign-no-right-turn-fill::before { content: "\f863"; } +.bi-sign-no-right-turn::before { content: "\f864"; } +.bi-sign-railroad-fill::before { content: "\f865"; } +.bi-sign-railroad::before { content: "\f866"; } +.bi-building-add::before { content: "\f867"; } +.bi-building-check::before { content: "\f868"; } +.bi-building-dash::before { content: "\f869"; } +.bi-building-down::before { content: "\f86a"; } +.bi-building-exclamation::before { content: "\f86b"; } +.bi-building-fill-add::before { content: "\f86c"; } +.bi-building-fill-check::before { content: "\f86d"; } +.bi-building-fill-dash::before { content: "\f86e"; } +.bi-building-fill-down::before { content: "\f86f"; } +.bi-building-fill-exclamation::before { content: "\f870"; } +.bi-building-fill-gear::before { content: "\f871"; } +.bi-building-fill-lock::before { content: "\f872"; } +.bi-building-fill-slash::before { content: "\f873"; } +.bi-building-fill-up::before { content: "\f874"; } +.bi-building-fill-x::before { content: "\f875"; } +.bi-building-fill::before { content: "\f876"; } +.bi-building-gear::before { content: "\f877"; } +.bi-building-lock::before { content: "\f878"; } +.bi-building-slash::before { content: "\f879"; } +.bi-building-up::before { content: "\f87a"; } +.bi-building-x::before { content: "\f87b"; } +.bi-buildings-fill::before { content: "\f87c"; } +.bi-buildings::before { content: "\f87d"; } +.bi-bus-front-fill::before { content: "\f87e"; } +.bi-bus-front::before { content: "\f87f"; } +.bi-ev-front-fill::before { content: "\f880"; } +.bi-ev-front::before { content: "\f881"; } +.bi-globe-americas::before { content: "\f882"; } +.bi-globe-asia-australia::before { content: "\f883"; } +.bi-globe-central-south-asia::before { content: "\f884"; } +.bi-globe-europe-africa::before { content: "\f885"; } +.bi-house-add-fill::before { content: "\f886"; } +.bi-house-add::before { content: "\f887"; } +.bi-house-check-fill::before { content: "\f888"; } +.bi-house-check::before { content: "\f889"; } +.bi-house-dash-fill::before { content: "\f88a"; } +.bi-house-dash::before { content: "\f88b"; } +.bi-house-down-fill::before { content: "\f88c"; } +.bi-house-down::before { content: "\f88d"; } +.bi-house-exclamation-fill::before { content: "\f88e"; } +.bi-house-exclamation::before { content: "\f88f"; } +.bi-house-gear-fill::before { content: "\f890"; } +.bi-house-gear::before { content: "\f891"; } +.bi-house-lock-fill::before { content: "\f892"; } +.bi-house-lock::before { content: "\f893"; } +.bi-house-slash-fill::before { content: "\f894"; } +.bi-house-slash::before { content: "\f895"; } +.bi-house-up-fill::before { content: "\f896"; } +.bi-house-up::before { content: "\f897"; } +.bi-house-x-fill::before { content: "\f898"; } +.bi-house-x::before { content: "\f899"; } +.bi-person-add::before { content: "\f89a"; } +.bi-person-down::before { content: "\f89b"; } +.bi-person-exclamation::before { content: "\f89c"; } +.bi-person-fill-add::before { content: "\f89d"; } +.bi-person-fill-check::before { content: "\f89e"; } +.bi-person-fill-dash::before { content: "\f89f"; } +.bi-person-fill-down::before { content: "\f8a0"; } +.bi-person-fill-exclamation::before { content: "\f8a1"; } +.bi-person-fill-gear::before { content: "\f8a2"; } +.bi-person-fill-lock::before { content: "\f8a3"; } +.bi-person-fill-slash::before { content: "\f8a4"; } +.bi-person-fill-up::before { content: "\f8a5"; } +.bi-person-fill-x::before { content: "\f8a6"; } +.bi-person-gear::before { content: "\f8a7"; } +.bi-person-lock::before { content: "\f8a8"; } +.bi-person-slash::before { content: "\f8a9"; } +.bi-person-up::before { content: "\f8aa"; } +.bi-scooter::before { content: "\f8ab"; } +.bi-taxi-front-fill::before { content: "\f8ac"; } +.bi-taxi-front::before { content: "\f8ad"; } +.bi-amd::before { content: "\f8ae"; } +.bi-database-add::before { content: "\f8af"; } +.bi-database-check::before { content: "\f8b0"; } +.bi-database-dash::before { content: "\f8b1"; } +.bi-database-down::before { content: "\f8b2"; } +.bi-database-exclamation::before { content: "\f8b3"; } +.bi-database-fill-add::before { content: "\f8b4"; } +.bi-database-fill-check::before { content: "\f8b5"; } +.bi-database-fill-dash::before { content: "\f8b6"; } +.bi-database-fill-down::before { content: "\f8b7"; } +.bi-database-fill-exclamation::before { content: "\f8b8"; } +.bi-database-fill-gear::before { content: "\f8b9"; } +.bi-database-fill-lock::before { content: "\f8ba"; } +.bi-database-fill-slash::before { content: "\f8bb"; } +.bi-database-fill-up::before { content: "\f8bc"; } +.bi-database-fill-x::before { content: "\f8bd"; } +.bi-database-fill::before { content: "\f8be"; } +.bi-database-gear::before { content: "\f8bf"; } +.bi-database-lock::before { content: "\f8c0"; } +.bi-database-slash::before { content: "\f8c1"; } +.bi-database-up::before { content: "\f8c2"; } +.bi-database-x::before { content: "\f8c3"; } +.bi-database::before { content: "\f8c4"; } +.bi-houses-fill::before { content: "\f8c5"; } +.bi-houses::before { content: "\f8c6"; } +.bi-nvidia::before { content: "\f8c7"; } +.bi-person-vcard-fill::before { content: "\f8c8"; } +.bi-person-vcard::before { content: "\f8c9"; } +.bi-sina-weibo::before { content: "\f8ca"; } +.bi-tencent-qq::before { content: "\f8cb"; } +.bi-wikipedia::before { content: "\f8cc"; } +.bi-alphabet-uppercase::before { content: "\f2a5"; } +.bi-alphabet::before { content: "\f68a"; } +.bi-amazon::before { content: "\f68d"; } +.bi-arrows-collapse-vertical::before { content: "\f690"; } +.bi-arrows-expand-vertical::before { content: "\f695"; } +.bi-arrows-vertical::before { content: "\f698"; } +.bi-arrows::before { content: "\f6a2"; } +.bi-ban-fill::before { content: "\f6a3"; } +.bi-ban::before { content: "\f6b6"; } +.bi-bing::before { content: "\f6c2"; } +.bi-cake::before { content: "\f6e0"; } +.bi-cake2::before { content: "\f6ed"; } +.bi-cookie::before { content: "\f6ee"; } +.bi-copy::before { content: "\f759"; } +.bi-crosshair::before { content: "\f769"; } +.bi-crosshair2::before { content: "\f794"; } +.bi-emoji-astonished-fill::before { content: "\f795"; } +.bi-emoji-astonished::before { content: "\f79a"; } +.bi-emoji-grimace-fill::before { content: "\f79b"; } +.bi-emoji-grimace::before { content: "\f7a0"; } +.bi-emoji-grin-fill::before { content: "\f7a1"; } +.bi-emoji-grin::before { content: "\f7a6"; } +.bi-emoji-surprise-fill::before { content: "\f7a7"; } +.bi-emoji-surprise::before { content: "\f7ac"; } +.bi-emoji-tear-fill::before { content: "\f7ad"; } +.bi-emoji-tear::before { content: "\f7b2"; } +.bi-envelope-arrow-down-fill::before { content: "\f7b3"; } +.bi-envelope-arrow-down::before { content: "\f7b8"; } +.bi-envelope-arrow-up-fill::before { content: "\f7b9"; } +.bi-envelope-arrow-up::before { content: "\f7be"; } +.bi-feather::before { content: "\f7bf"; } +.bi-feather2::before { content: "\f7c4"; } +.bi-floppy-fill::before { content: "\f7c5"; } +.bi-floppy::before { content: "\f7d8"; } +.bi-floppy2-fill::before { content: "\f7d9"; } +.bi-floppy2::before { content: "\f7e4"; } +.bi-gitlab::before { content: "\f7e5"; } +.bi-highlighter::before { content: "\f7f8"; } +.bi-marker-tip::before { content: "\f802"; } +.bi-nvme-fill::before { content: "\f803"; } +.bi-nvme::before { content: "\f80c"; } +.bi-opencollective::before { content: "\f80d"; } +.bi-pci-card-network::before { content: "\f8cd"; } +.bi-pci-card-sound::before { content: "\f8ce"; } +.bi-radar::before { content: "\f8cf"; } +.bi-send-arrow-down-fill::before { content: "\f8d0"; } +.bi-send-arrow-down::before { content: "\f8d1"; } +.bi-send-arrow-up-fill::before { content: "\f8d2"; } +.bi-send-arrow-up::before { content: "\f8d3"; } +.bi-sim-slash-fill::before { content: "\f8d4"; } +.bi-sim-slash::before { content: "\f8d5"; } +.bi-sourceforge::before { content: "\f8d6"; } +.bi-substack::before { content: "\f8d7"; } +.bi-threads-fill::before { content: "\f8d8"; } +.bi-threads::before { content: "\f8d9"; } +.bi-transparency::before { content: "\f8da"; } +.bi-twitter-x::before { content: "\f8db"; } +.bi-type-h4::before { content: "\f8dc"; } +.bi-type-h5::before { content: "\f8dd"; } +.bi-type-h6::before { content: "\f8de"; } +.bi-backpack-fill::before { content: "\f8df"; } +.bi-backpack::before { content: "\f8e0"; } +.bi-backpack2-fill::before { content: "\f8e1"; } +.bi-backpack2::before { content: "\f8e2"; } +.bi-backpack3-fill::before { content: "\f8e3"; } +.bi-backpack3::before { content: "\f8e4"; } +.bi-backpack4-fill::before { content: "\f8e5"; } +.bi-backpack4::before { content: "\f8e6"; } +.bi-brilliance::before { content: "\f8e7"; } +.bi-cake-fill::before { content: "\f8e8"; } +.bi-cake2-fill::before { content: "\f8e9"; } +.bi-duffle-fill::before { content: "\f8ea"; } +.bi-duffle::before { content: "\f8eb"; } +.bi-exposure::before { content: "\f8ec"; } +.bi-gender-neuter::before { content: "\f8ed"; } +.bi-highlights::before { content: "\f8ee"; } +.bi-luggage-fill::before { content: "\f8ef"; } +.bi-luggage::before { content: "\f8f0"; } +.bi-mailbox-flag::before { content: "\f8f1"; } +.bi-mailbox2-flag::before { content: "\f8f2"; } +.bi-noise-reduction::before { content: "\f8f3"; } +.bi-passport-fill::before { content: "\f8f4"; } +.bi-passport::before { content: "\f8f5"; } +.bi-person-arms-up::before { content: "\f8f6"; } +.bi-person-raised-hand::before { content: "\f8f7"; } +.bi-person-standing-dress::before { content: "\f8f8"; } +.bi-person-standing::before { content: "\f8f9"; } +.bi-person-walking::before { content: "\f8fa"; } +.bi-person-wheelchair::before { content: "\f8fb"; } +.bi-shadows::before { content: "\f8fc"; } +.bi-suitcase-fill::before { content: "\f8fd"; } +.bi-suitcase-lg-fill::before { content: "\f8fe"; } +.bi-suitcase-lg::before { content: "\f8ff"; } +.bi-suitcase::before { content: "\f900"; } +.bi-suitcase2-fill::before { content: "\f901"; } +.bi-suitcase2::before { content: "\f902"; } +.bi-vignette::before { content: "\f903"; } diff --git a/_site/site_libs/bootstrap/bootstrap-icons.woff b/_site/site_libs/bootstrap/bootstrap-icons.woff new file mode 100644 index 0000000..dbeeb05 Binary files /dev/null and b/_site/site_libs/bootstrap/bootstrap-icons.woff differ diff --git a/_site/site_libs/bootstrap/bootstrap.min.css b/_site/site_libs/bootstrap/bootstrap.min.css new file mode 100644 index 0000000..706ad9d --- /dev/null +++ b/_site/site_libs/bootstrap/bootstrap.min.css @@ -0,0 +1,12 @@ +/*! + * Bootstrap v5.3.1 (https://getbootstrap.com/) + * Copyright 2011-2023 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */@import"https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@300;400;700&display=swap";:root,[data-bs-theme=light]{--bs-blue: #2780e3;--bs-indigo: #6610f2;--bs-purple: #613d7c;--bs-pink: #e83e8c;--bs-red: #ff0039;--bs-orange: #f0ad4e;--bs-yellow: #ff7518;--bs-green: #3fb618;--bs-teal: #20c997;--bs-cyan: #9954bb;--bs-black: #000;--bs-white: #fff;--bs-gray: #6c757d;--bs-gray-dark: #343a40;--bs-gray-100: #f8f9fa;--bs-gray-200: #e9ecef;--bs-gray-300: #dee2e6;--bs-gray-400: #ced4da;--bs-gray-500: #adb5bd;--bs-gray-600: #6c757d;--bs-gray-700: #495057;--bs-gray-800: #343a40;--bs-gray-900: #212529;--bs-default: #343a40;--bs-primary: #2780e3;--bs-secondary: #343a40;--bs-success: #3fb618;--bs-info: #9954bb;--bs-warning: #ff7518;--bs-danger: #ff0039;--bs-light: #f8f9fa;--bs-dark: #343a40;--bs-default-rgb: 52, 58, 64;--bs-primary-rgb: 39, 128, 227;--bs-secondary-rgb: 52, 58, 64;--bs-success-rgb: 63, 182, 24;--bs-info-rgb: 153, 84, 187;--bs-warning-rgb: 255, 117, 24;--bs-danger-rgb: 255, 0, 57;--bs-light-rgb: 248, 249, 250;--bs-dark-rgb: 52, 58, 64;--bs-primary-text-emphasis: #10335b;--bs-secondary-text-emphasis: #15171a;--bs-success-text-emphasis: #19490a;--bs-info-text-emphasis: #3d224b;--bs-warning-text-emphasis: #662f0a;--bs-danger-text-emphasis: #660017;--bs-light-text-emphasis: #495057;--bs-dark-text-emphasis: #495057;--bs-primary-bg-subtle: #d4e6f9;--bs-secondary-bg-subtle: #d6d8d9;--bs-success-bg-subtle: #d9f0d1;--bs-info-bg-subtle: #ebddf1;--bs-warning-bg-subtle: #ffe3d1;--bs-danger-bg-subtle: #ffccd7;--bs-light-bg-subtle: #fcfcfd;--bs-dark-bg-subtle: #ced4da;--bs-primary-border-subtle: #a9ccf4;--bs-secondary-border-subtle: #aeb0b3;--bs-success-border-subtle: #b2e2a3;--bs-info-border-subtle: #d6bbe4;--bs-warning-border-subtle: #ffc8a3;--bs-danger-border-subtle: #ff99b0;--bs-light-border-subtle: #e9ecef;--bs-dark-border-subtle: #adb5bd;--bs-white-rgb: 255, 255, 255;--bs-black-rgb: 0, 0, 0;--bs-font-sans-serif: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-root-font-size: 17px;--bs-body-font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";--bs-body-font-size:1rem;--bs-body-font-weight: 400;--bs-body-line-height: 1.5;--bs-body-color: #343a40;--bs-body-color-rgb: 52, 58, 64;--bs-body-bg: #fff;--bs-body-bg-rgb: 255, 255, 255;--bs-emphasis-color: #000;--bs-emphasis-color-rgb: 0, 0, 0;--bs-secondary-color: rgba(52, 58, 64, 0.75);--bs-secondary-color-rgb: 52, 58, 64;--bs-secondary-bg: #e9ecef;--bs-secondary-bg-rgb: 233, 236, 239;--bs-tertiary-color: rgba(52, 58, 64, 0.5);--bs-tertiary-color-rgb: 52, 58, 64;--bs-tertiary-bg: #f8f9fa;--bs-tertiary-bg-rgb: 248, 249, 250;--bs-heading-color: inherit;--bs-link-color: #2761e3;--bs-link-color-rgb: 39, 97, 227;--bs-link-decoration: underline;--bs-link-hover-color: #1f4eb6;--bs-link-hover-color-rgb: 31, 78, 182;--bs-code-color: #7d12ba;--bs-highlight-bg: #ffe3d1;--bs-border-width: 1px;--bs-border-style: solid;--bs-border-color: #dee2e6;--bs-border-color-translucent: rgba(0, 0, 0, 0.175);--bs-border-radius: 0.25rem;--bs-border-radius-sm: 0.2em;--bs-border-radius-lg: 0.5rem;--bs-border-radius-xl: 1rem;--bs-border-radius-xxl: 2rem;--bs-border-radius-2xl: var(--bs-border-radius-xxl);--bs-border-radius-pill: 50rem;--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width: 0.25rem;--bs-focus-ring-opacity: 0.25;--bs-focus-ring-color: rgba(39, 128, 227, 0.25);--bs-form-valid-color: #3fb618;--bs-form-valid-border-color: #3fb618;--bs-form-invalid-color: #ff0039;--bs-form-invalid-border-color: #ff0039}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color: #dee2e6;--bs-body-color-rgb: 222, 226, 230;--bs-body-bg: #212529;--bs-body-bg-rgb: 33, 37, 41;--bs-emphasis-color: #fff;--bs-emphasis-color-rgb: 255, 255, 255;--bs-secondary-color: rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb: 222, 226, 230;--bs-secondary-bg: #343a40;--bs-secondary-bg-rgb: 52, 58, 64;--bs-tertiary-color: rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb: 222, 226, 230;--bs-tertiary-bg: #2b3035;--bs-tertiary-bg-rgb: 43, 48, 53;--bs-primary-text-emphasis: #7db3ee;--bs-secondary-text-emphasis: #85898c;--bs-success-text-emphasis: #8cd374;--bs-info-text-emphasis: #c298d6;--bs-warning-text-emphasis: #ffac74;--bs-danger-text-emphasis: #ff6688;--bs-light-text-emphasis: #f8f9fa;--bs-dark-text-emphasis: #dee2e6;--bs-primary-bg-subtle: #081a2d;--bs-secondary-bg-subtle: #0a0c0d;--bs-success-bg-subtle: #0d2405;--bs-info-bg-subtle: #1f1125;--bs-warning-bg-subtle: #331705;--bs-danger-bg-subtle: #33000b;--bs-light-bg-subtle: #343a40;--bs-dark-bg-subtle: #1a1d20;--bs-primary-border-subtle: #174d88;--bs-secondary-border-subtle: #1f2326;--bs-success-border-subtle: #266d0e;--bs-info-border-subtle: #5c3270;--bs-warning-border-subtle: #99460e;--bs-danger-border-subtle: #990022;--bs-light-border-subtle: #495057;--bs-dark-border-subtle: #343a40;--bs-heading-color: inherit;--bs-link-color: #7db3ee;--bs-link-hover-color: #97c2f1;--bs-link-color-rgb: 125, 179, 238;--bs-link-hover-color-rgb: 151, 194, 241;--bs-code-color: white;--bs-border-color: #495057;--bs-border-color-translucent: rgba(255, 255, 255, 0.15);--bs-form-valid-color: #8cd374;--bs-form-valid-border-color: #8cd374;--bs-form-invalid-color: #ff6688;--bs-form-invalid-border-color: #ff6688}*,*::before,*::after{box-sizing:border-box}:root{font-size:var(--bs-root-font-size)}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}hr{margin:1rem 0;color:inherit;border:0;border-top:1px solid;opacity:.25}h6,.h6,h5,.h5,h4,.h4,h3,.h3,h2,.h2,h1,.h1{margin-top:0;margin-bottom:.5rem;font-weight:400;line-height:1.2;color:var(--bs-heading-color)}h1,.h1{font-size:calc(1.325rem + 0.9vw)}@media(min-width: 1200px){h1,.h1{font-size:2rem}}h2,.h2{font-size:calc(1.29rem + 0.48vw)}@media(min-width: 1200px){h2,.h2{font-size:1.65rem}}h3,.h3{font-size:calc(1.27rem + 0.24vw)}@media(min-width: 1200px){h3,.h3{font-size:1.45rem}}h4,.h4{font-size:1.25rem}h5,.h5{font-size:1.1rem}h6,.h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{text-decoration:underline dotted;-webkit-text-decoration:underline dotted;-moz-text-decoration:underline dotted;-ms-text-decoration:underline dotted;-o-text-decoration:underline dotted;cursor:help;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem;padding:.625rem 1.25rem;border-left:.25rem solid #e9ecef}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}b,strong{font-weight:bolder}small,.small{font-size:0.875em}mark,.mark{padding:.1875em;background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:0.75em;line-height:0;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}a{color:rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));text-decoration:underline;-webkit-text-decoration:underline;-moz-text-decoration:underline;-ms-text-decoration:underline;-o-text-decoration:underline}a:hover{--bs-link-color-rgb: var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}pre,code,kbd,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:0.875em;color:#000;background-color:#f8f9fa;padding:.5rem;border:1px solid var(--bs-border-color, #dee2e6)}pre code{background-color:rgba(0,0,0,0);font-size:inherit;color:inherit;word-break:normal}code{font-size:0.875em;color:var(--bs-code-color);background-color:#f8f9fa;padding:.125rem .25rem;word-wrap:break-word}a>code{color:inherit}kbd{padding:.4rem .4rem;font-size:0.875em;color:#fff;background-color:#343a40}kbd kbd{padding:0;font-size:1em}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:rgba(52,58,64,.75);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]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none !important}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]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none !important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media(min-width: 1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media(min-width: 1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media(min-width: 1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media(min-width: 1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media(min-width: 1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media(min-width: 1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:0.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:0.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:0.875em;color:rgba(52,58,64,.75)}.container,.container-fluid,.container-xxl,.container-xl,.container-lg,.container-md,.container-sm{--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;width:100%;padding-right:calc(var(--bs-gutter-x)*.5);padding-left:calc(var(--bs-gutter-x)*.5);margin-right:auto;margin-left:auto}@media(min-width: 576px){.container-sm,.container{max-width:540px}}@media(min-width: 768px){.container-md,.container-sm,.container{max-width:720px}}@media(min-width: 992px){.container-lg,.container-md,.container-sm,.container{max-width:960px}}@media(min-width: 1200px){.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1140px}}@media(min-width: 1400px){.container-xxl,.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1320px}}:root{--bs-breakpoint-xs: 0;--bs-breakpoint-sm: 576px;--bs-breakpoint-md: 768px;--bs-breakpoint-lg: 992px;--bs-breakpoint-xl: 1200px;--bs-breakpoint-xxl: 1400px}.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-color-type: initial;--bs-table-bg-type: initial;--bs-table-color-state: initial;--bs-table-bg-state: initial;--bs-table-color: #343a40;--bs-table-bg: #fff;--bs-table-border-color: #dee2e6;--bs-table-accent-bg: transparent;--bs-table-striped-color: #343a40;--bs-table-striped-bg: rgba(0, 0, 0, 0.05);--bs-table-active-color: #343a40;--bs-table-active-bg: rgba(0, 0, 0, 0.1);--bs-table-hover-color: #343a40;--bs-table-hover-bg: rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem .5rem;color:var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg)))}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(1px*2) solid #b2bac1}.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-color-type: var(--bs-table-striped-color);--bs-table-bg-type: var(--bs-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(even){--bs-table-color-type: var(--bs-table-striped-color);--bs-table-bg-type: var(--bs-table-striped-bg)}.table-active{--bs-table-color-state: var(--bs-table-active-color);--bs-table-bg-state: var(--bs-table-active-bg)}.table-hover>tbody>tr:hover>*{--bs-table-color-state: var(--bs-table-hover-color);--bs-table-bg-state: var(--bs-table-hover-bg)}.table-primary{--bs-table-color: #000;--bs-table-bg: #d4e6f9;--bs-table-border-color: #bfcfe0;--bs-table-striped-bg: #c9dbed;--bs-table-striped-color: #000;--bs-table-active-bg: #bfcfe0;--bs-table-active-color: #000;--bs-table-hover-bg: #c4d5e6;--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color: #000;--bs-table-bg: #d6d8d9;--bs-table-border-color: #c1c2c3;--bs-table-striped-bg: #cbcdce;--bs-table-striped-color: #000;--bs-table-active-bg: #c1c2c3;--bs-table-active-color: #000;--bs-table-hover-bg: #c6c8c9;--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color: #000;--bs-table-bg: #d9f0d1;--bs-table-border-color: #c3d8bc;--bs-table-striped-bg: #cee4c7;--bs-table-striped-color: #000;--bs-table-active-bg: #c3d8bc;--bs-table-active-color: #000;--bs-table-hover-bg: #c9dec1;--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color: #000;--bs-table-bg: #ebddf1;--bs-table-border-color: #d4c7d9;--bs-table-striped-bg: #dfd2e5;--bs-table-striped-color: #000;--bs-table-active-bg: #d4c7d9;--bs-table-active-color: #000;--bs-table-hover-bg: #d9ccdf;--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color: #000;--bs-table-bg: #ffe3d1;--bs-table-border-color: #e6ccbc;--bs-table-striped-bg: #f2d8c7;--bs-table-striped-color: #000;--bs-table-active-bg: #e6ccbc;--bs-table-active-color: #000;--bs-table-hover-bg: #ecd2c1;--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color: #000;--bs-table-bg: #ffccd7;--bs-table-border-color: #e6b8c2;--bs-table-striped-bg: #f2c2cc;--bs-table-striped-color: #000;--bs-table-active-bg: #e6b8c2;--bs-table-active-color: #000;--bs-table-hover-bg: #ecbdc7;--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color: #000;--bs-table-bg: #f8f9fa;--bs-table-border-color: #dfe0e1;--bs-table-striped-bg: #ecedee;--bs-table-striped-color: #000;--bs-table-active-bg: #dfe0e1;--bs-table-active-color: #000;--bs-table-hover-bg: #e5e6e7;--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color: #fff;--bs-table-bg: #343a40;--bs-table-border-color: #484e53;--bs-table-striped-bg: #3e444a;--bs-table-striped-color: #fff;--bs-table-active-bg: #484e53;--bs-table-active-color: #fff;--bs-table-hover-bg: #43494e;--bs-table-hover-color: #fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.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:rgba(52,58,64,.75)}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#343a40;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;background-color:#fff;background-clip:padding-box;border:1px solid #dee2e6;border-radius:0;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#343a40;background-color:#fff;border-color:#93c0f1;outline:0;box-shadow:0 0 0 .25rem rgba(39,128,227,.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::placeholder{color:rgba(52,58,64,.75);opacity:1}.form-control:disabled{background-color:#e9ecef;opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-0.375rem -0.75rem;margin-inline-end:.75rem;color:#343a40;background-color:#f8f9fa;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:#e9ecef}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#343a40;background-color:rgba(0,0,0,0);border:solid rgba(0,0,0,0);border-width:1px 0}.form-control-plaintext:focus{outline: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 + calc(1px * 2));padding:.25rem .5rem;font-size:0.875rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-0.25rem -0.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(1px * 2));padding:.5rem 1rem;font-size:1.25rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-0.5rem -1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + 0.75rem + calc(1px * 2))}textarea.form-control-sm{min-height:calc(1.5em + 0.5rem + calc(1px * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(1px * 2))}.form-control-color{width:3rem;height:calc(1.5em + 0.75rem + calc(1px * 2));padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0 !important}.form-control-color::-webkit-color-swatch{border:0 !important}.form-control-color.form-control-sm{height:calc(1.5em + 0.5rem + calc(1px * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(1px * 2))}.form-select{--bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#343a40;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;background-color:#fff;background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon, none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #dee2e6;border-radius:0;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-select{transition:none}}.form-select:focus{border-color:#93c0f1;outline:0;box-shadow:0 0 0 .25rem rgba(39,128,227,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:rgba(0,0,0,0);text-shadow:0 0 0 #343a40}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:0.875rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.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-reverse{padding-right:0;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:0;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{--bs-form-check-bg: #fff;width:1em;height:1em;margin-top:.25em;vertical-align:top;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid #dee2e6;print-color-adjust:exact}.form-check-input[type=radio],.shiny-input-container .checkbox input[type=radio],.shiny-input-container .checkbox-inline input[type=radio],.shiny-input-container .radio input[type=radio],.shiny-input-container .radio-inline input[type=radio]{border-radius:50%}.form-check-input:active,.shiny-input-container .checkbox input:active,.shiny-input-container .checkbox-inline input:active,.shiny-input-container .radio input:active,.shiny-input-container .radio-inline input:active{filter:brightness(90%)}.form-check-input:focus,.shiny-input-container .checkbox input:focus,.shiny-input-container .checkbox-inline input:focus,.shiny-input-container .radio input:focus,.shiny-input-container .radio-inline input:focus{border-color:#93c0f1;outline:0;box-shadow:0 0 0 .25rem rgba(39,128,227,.25)}.form-check-input:checked,.shiny-input-container .checkbox input:checked,.shiny-input-container .checkbox-inline input:checked,.shiny-input-container .radio input:checked,.shiny-input-container .radio-inline input:checked{background-color:#2780e3;border-color:#2780e3}.form-check-input:checked[type=checkbox],.shiny-input-container .checkbox input:checked[type=checkbox],.shiny-input-container .checkbox-inline input:checked[type=checkbox],.shiny-input-container .radio input:checked[type=checkbox],.shiny-input-container .radio-inline input:checked[type=checkbox]{--bs-form-check-bg-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 10 3 3 6-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]{--bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate,.shiny-input-container .checkbox input[type=checkbox]:indeterminate,.shiny-input-container .checkbox-inline input[type=checkbox]:indeterminate,.shiny-input-container .radio input[type=checkbox]:indeterminate,.shiny-input-container .radio-inline input[type=checkbox]:indeterminate{background-color:#2780e3;border-color:#2780e3;--bs-form-check-bg-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{cursor:default;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{--bs-form-switch-bg: 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");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:left center;transition:background-position .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{--bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2393c0f1'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;--bs-form-switch-bg: 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-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-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}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;background-color:rgba(0,0,0,0)}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(39,128,227,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(39,128,227,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-0.25rem;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;background-color:#2780e3;border:0;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-range::-webkit-slider-thumb{transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#bed9f7}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:rgba(0,0,0,0);cursor:pointer;background-color:#f8f9fa;border-color:rgba(0,0,0,0)}.form-range::-moz-range-thumb{width:1rem;height:1rem;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;background-color:#2780e3;border:0;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-range::-moz-range-thumb{transition:none}}.form-range::-moz-range-thumb:active{background-color:#bed9f7}.form-range::-moz-range-track{width:100%;height:.5rem;color:rgba(0,0,0,0);cursor:pointer;background-color:#f8f9fa;border-color:rgba(0,0,0,0)}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:rgba(52,58,64,.75)}.form-range:disabled::-moz-range-thumb{background-color:rgba(52,58,64,.75)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(1px * 2));min-height:calc(3.5rem + calc(1px * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;height:100%;padding:1rem .75rem;overflow:hidden;text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:1px solid rgba(0,0,0,0);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,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control::placeholder,.form-floating>.form-control-plaintext::placeholder{color:rgba(0,0,0,0)}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown),.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill,.form-floating>.form-control-plaintext:-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-control-plaintext~label,.form-floating>.form-select~label{color:rgba(var(--bs-body-color-rgb), 0.65);transform:scale(0.85) translateY(-0.5rem) translateX(0.15rem)}.form-floating>.form-control:focus~label::after,.form-floating>.form-control:not(:placeholder-shown)~label::after,.form-floating>.form-control-plaintext~label::after,.form-floating>.form-select~label::after{position:absolute;inset:1rem .375rem;z-index:-1;height:1.5em;content:"";background-color:#fff}.form-floating>.form-control:-webkit-autofill~label{color:rgba(var(--bs-body-color-rgb), 0.65);transform:scale(0.85) translateY(-0.5rem) translateX(0.15rem)}.form-floating>.form-control-plaintext~label{border-width:1px 0}.form-floating>:disabled~label,.form-floating>.form-control:disabled~label{color:#6c757d}.form-floating>:disabled~label::after,.form-floating>.form-control:disabled~label::after{background-color:#e9ecef}.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,.input-group>.form-floating{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,.input-group>.form-floating:focus-within{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.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:#343a40;text-align:center;white-space:nowrap;background-color:#f8f9fa;border:1px solid #dee2e6}.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text,.input-group-lg>.btn{padding:.5rem 1rem;font-size:1.25rem}.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text,.input-group-sm>.btn{padding:.25rem .5rem;font-size:0.875rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(1px*-1)}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:0.875em;color:#3fb618}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:0.875rem;color:#fff;background-color:#3fb618}.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip,.is-valid~.valid-feedback,.is-valid~.valid-tooltip{display:block}.was-validated .form-control:valid,.form-control.is-valid{border-color:#3fb618;padding-right:calc(1.5em + 0.75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%233fb618' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(0.375em + 0.1875rem) center;background-size:calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:#3fb618;box-shadow:0 0 0 .25rem rgba(63,182,24,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + 0.75rem);background-position:top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem)}.was-validated .form-select:valid,.form-select.is-valid{border-color:#3fb618}.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"],.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"]{--bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%233fb618' d='M2.3 6.73.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");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .form-select:valid:focus,.form-select.is-valid:focus{border-color:#3fb618;box-shadow:0 0 0 .25rem rgba(63,182,24,.25)}.was-validated .form-control-color:valid,.form-control-color.is-valid{width:calc(3rem + calc(1.5em + 0.75rem))}.was-validated .form-check-input:valid,.form-check-input.is-valid{border-color:#3fb618}.was-validated .form-check-input:valid:checked,.form-check-input.is-valid:checked{background-color:#3fb618}.was-validated .form-check-input:valid:focus,.form-check-input.is-valid:focus{box-shadow:0 0 0 .25rem rgba(63,182,24,.25)}.was-validated .form-check-input:valid~.form-check-label,.form-check-input.is-valid~.form-check-label{color:#3fb618}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):valid,.input-group>.form-control:not(:focus).is-valid,.was-validated .input-group>.form-select:not(:focus):valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.input-group>.form-floating:not(:focus-within).is-valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:0.875em;color:#ff0039}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:0.875rem;color:#fff;background-color:#ff0039}.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip,.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip{display:block}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:#ff0039;padding-right:calc(1.5em + 0.75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23ff0039'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23ff0039' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(0.375em + 0.1875rem) center;background-size:calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:#ff0039;box-shadow:0 0 0 .25rem rgba(255,0,57,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + 0.75rem);background-position:top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem)}.was-validated .form-select:invalid,.form-select.is-invalid{border-color:#ff0039}.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"],.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23ff0039'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23ff0039' stroke='none'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .form-select:invalid:focus,.form-select.is-invalid:focus{border-color:#ff0039;box-shadow:0 0 0 .25rem rgba(255,0,57,.25)}.was-validated .form-control-color:invalid,.form-control-color.is-invalid{width:calc(3rem + calc(1.5em + 0.75rem))}.was-validated .form-check-input:invalid,.form-check-input.is-invalid{border-color:#ff0039}.was-validated .form-check-input:invalid:checked,.form-check-input.is-invalid:checked{background-color:#ff0039}.was-validated .form-check-input:invalid:focus,.form-check-input.is-invalid:focus{box-shadow:0 0 0 .25rem rgba(255,0,57,.25)}.was-validated .form-check-input:invalid~.form-check-label,.form-check-input.is-invalid~.form-check-label{color:#ff0039}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):invalid,.input-group>.form-control:not(:focus).is-invalid,.was-validated .input-group>.form-select:not(:focus):invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.input-group>.form-floating:not(:focus-within).is-invalid{z-index:4}.btn{--bs-btn-padding-x: 0.75rem;--bs-btn-padding-y: 0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight: 400;--bs-btn-line-height: 1.5;--bs-btn-color: #343a40;--bs-btn-bg: transparent;--bs-btn-border-width: 1px;--bs-btn-border-color: transparent;--bs-btn-border-radius: 0.25rem;--bs-btn-hover-border-color: transparent;--bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity: 0.65;--bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);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;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);background-color:var(--bs-btn-bg);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:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,:not(.btn-check)+.btn:active,.btn:first-child:active,.btn.active,.btn.show{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,:not(.btn-check)+.btn:active:focus-visible,.btn:first-child:active:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn:disabled,.btn.disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-default{--bs-btn-color: #fff;--bs-btn-bg: #343a40;--bs-btn-border-color: #343a40;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #2c3136;--bs-btn-hover-border-color: #2a2e33;--bs-btn-focus-shadow-rgb: 82, 88, 93;--bs-btn-active-color: #fff;--bs-btn-active-bg: #2a2e33;--bs-btn-active-border-color: #272c30;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #343a40;--bs-btn-disabled-border-color: #343a40}.btn-primary{--bs-btn-color: #fff;--bs-btn-bg: #2780e3;--bs-btn-border-color: #2780e3;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #216dc1;--bs-btn-hover-border-color: #1f66b6;--bs-btn-focus-shadow-rgb: 71, 147, 231;--bs-btn-active-color: #fff;--bs-btn-active-bg: #1f66b6;--bs-btn-active-border-color: #1d60aa;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #2780e3;--bs-btn-disabled-border-color: #2780e3}.btn-secondary{--bs-btn-color: #fff;--bs-btn-bg: #343a40;--bs-btn-border-color: #343a40;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #2c3136;--bs-btn-hover-border-color: #2a2e33;--bs-btn-focus-shadow-rgb: 82, 88, 93;--bs-btn-active-color: #fff;--bs-btn-active-bg: #2a2e33;--bs-btn-active-border-color: #272c30;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #343a40;--bs-btn-disabled-border-color: #343a40}.btn-success{--bs-btn-color: #fff;--bs-btn-bg: #3fb618;--bs-btn-border-color: #3fb618;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #369b14;--bs-btn-hover-border-color: #329213;--bs-btn-focus-shadow-rgb: 92, 193, 59;--bs-btn-active-color: #fff;--bs-btn-active-bg: #329213;--bs-btn-active-border-color: #2f8912;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #3fb618;--bs-btn-disabled-border-color: #3fb618}.btn-info{--bs-btn-color: #fff;--bs-btn-bg: #9954bb;--bs-btn-border-color: #9954bb;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #82479f;--bs-btn-hover-border-color: #7a4396;--bs-btn-focus-shadow-rgb: 168, 110, 197;--bs-btn-active-color: #fff;--bs-btn-active-bg: #7a4396;--bs-btn-active-border-color: #733f8c;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #9954bb;--bs-btn-disabled-border-color: #9954bb}.btn-warning{--bs-btn-color: #fff;--bs-btn-bg: #ff7518;--bs-btn-border-color: #ff7518;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #d96314;--bs-btn-hover-border-color: #cc5e13;--bs-btn-focus-shadow-rgb: 255, 138, 59;--bs-btn-active-color: #fff;--bs-btn-active-bg: #cc5e13;--bs-btn-active-border-color: #bf5812;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #ff7518;--bs-btn-disabled-border-color: #ff7518}.btn-danger{--bs-btn-color: #fff;--bs-btn-bg: #ff0039;--bs-btn-border-color: #ff0039;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #d90030;--bs-btn-hover-border-color: #cc002e;--bs-btn-focus-shadow-rgb: 255, 38, 87;--bs-btn-active-color: #fff;--bs-btn-active-bg: #cc002e;--bs-btn-active-border-color: #bf002b;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #ff0039;--bs-btn-disabled-border-color: #ff0039}.btn-light{--bs-btn-color: #000;--bs-btn-bg: #f8f9fa;--bs-btn-border-color: #f8f9fa;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #d3d4d5;--bs-btn-hover-border-color: #c6c7c8;--bs-btn-focus-shadow-rgb: 211, 212, 213;--bs-btn-active-color: #000;--bs-btn-active-bg: #c6c7c8;--bs-btn-active-border-color: #babbbc;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #000;--bs-btn-disabled-bg: #f8f9fa;--bs-btn-disabled-border-color: #f8f9fa}.btn-dark{--bs-btn-color: #fff;--bs-btn-bg: #343a40;--bs-btn-border-color: #343a40;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #52585d;--bs-btn-hover-border-color: #484e53;--bs-btn-focus-shadow-rgb: 82, 88, 93;--bs-btn-active-color: #fff;--bs-btn-active-bg: #5d6166;--bs-btn-active-border-color: #484e53;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #343a40;--bs-btn-disabled-border-color: #343a40}.btn-outline-default{--bs-btn-color: #343a40;--bs-btn-border-color: #343a40;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #343a40;--bs-btn-hover-border-color: #343a40;--bs-btn-focus-shadow-rgb: 52, 58, 64;--bs-btn-active-color: #fff;--bs-btn-active-bg: #343a40;--bs-btn-active-border-color: #343a40;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #343a40;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #343a40;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-primary{--bs-btn-color: #2780e3;--bs-btn-border-color: #2780e3;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #2780e3;--bs-btn-hover-border-color: #2780e3;--bs-btn-focus-shadow-rgb: 39, 128, 227;--bs-btn-active-color: #fff;--bs-btn-active-bg: #2780e3;--bs-btn-active-border-color: #2780e3;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #2780e3;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #2780e3;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-secondary{--bs-btn-color: #343a40;--bs-btn-border-color: #343a40;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #343a40;--bs-btn-hover-border-color: #343a40;--bs-btn-focus-shadow-rgb: 52, 58, 64;--bs-btn-active-color: #fff;--bs-btn-active-bg: #343a40;--bs-btn-active-border-color: #343a40;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #343a40;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #343a40;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-success{--bs-btn-color: #3fb618;--bs-btn-border-color: #3fb618;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #3fb618;--bs-btn-hover-border-color: #3fb618;--bs-btn-focus-shadow-rgb: 63, 182, 24;--bs-btn-active-color: #fff;--bs-btn-active-bg: #3fb618;--bs-btn-active-border-color: #3fb618;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #3fb618;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #3fb618;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-info{--bs-btn-color: #9954bb;--bs-btn-border-color: #9954bb;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #9954bb;--bs-btn-hover-border-color: #9954bb;--bs-btn-focus-shadow-rgb: 153, 84, 187;--bs-btn-active-color: #fff;--bs-btn-active-bg: #9954bb;--bs-btn-active-border-color: #9954bb;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #9954bb;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #9954bb;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-warning{--bs-btn-color: #ff7518;--bs-btn-border-color: #ff7518;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #ff7518;--bs-btn-hover-border-color: #ff7518;--bs-btn-focus-shadow-rgb: 255, 117, 24;--bs-btn-active-color: #fff;--bs-btn-active-bg: #ff7518;--bs-btn-active-border-color: #ff7518;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #ff7518;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #ff7518;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-danger{--bs-btn-color: #ff0039;--bs-btn-border-color: #ff0039;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #ff0039;--bs-btn-hover-border-color: #ff0039;--bs-btn-focus-shadow-rgb: 255, 0, 57;--bs-btn-active-color: #fff;--bs-btn-active-bg: #ff0039;--bs-btn-active-border-color: #ff0039;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #ff0039;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #ff0039;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-light{--bs-btn-color: #f8f9fa;--bs-btn-border-color: #f8f9fa;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #f8f9fa;--bs-btn-hover-border-color: #f8f9fa;--bs-btn-focus-shadow-rgb: 248, 249, 250;--bs-btn-active-color: #000;--bs-btn-active-bg: #f8f9fa;--bs-btn-active-border-color: #f8f9fa;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #f8f9fa;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #f8f9fa;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-dark{--bs-btn-color: #343a40;--bs-btn-border-color: #343a40;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #343a40;--bs-btn-hover-border-color: #343a40;--bs-btn-focus-shadow-rgb: 52, 58, 64;--bs-btn-active-color: #fff;--bs-btn-active-bg: #343a40;--bs-btn-active-border-color: #343a40;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #343a40;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #343a40;--bs-btn-bg: transparent;--bs-gradient: none}.btn-link{--bs-btn-font-weight: 400;--bs-btn-color: #2761e3;--bs-btn-bg: transparent;--bs-btn-border-color: transparent;--bs-btn-hover-color: #1f4eb6;--bs-btn-hover-border-color: transparent;--bs-btn-active-color: #1f4eb6;--bs-btn-active-border-color: transparent;--bs-btn-disabled-color: #6c757d;--bs-btn-disabled-border-color: transparent;--bs-btn-box-shadow: 0 0 0 #000;--bs-btn-focus-shadow-rgb: 71, 121, 231;text-decoration:underline;-webkit-text-decoration:underline;-moz-text-decoration:underline;-ms-text-decoration:underline;-o-text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-lg,.btn-group-lg>.btn{--bs-btn-padding-y: 0.5rem;--bs-btn-padding-x: 1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius: 0.5rem}.btn-sm,.btn-group-sm>.btn{--bs-btn-padding-y: 0.25rem;--bs-btn-padding-x: 0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius: 0.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,.dropup-center,.dropdown-center{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 rgba(0,0,0,0);border-bottom:0;border-left:.3em solid rgba(0,0,0,0)}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex: 1000;--bs-dropdown-min-width: 10rem;--bs-dropdown-padding-x: 0;--bs-dropdown-padding-y: 0.5rem;--bs-dropdown-spacer: 0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color: #343a40;--bs-dropdown-bg: #fff;--bs-dropdown-border-color: rgba(0, 0, 0, 0.175);--bs-dropdown-border-radius: 0.25rem;--bs-dropdown-border-width: 1px;--bs-dropdown-inner-border-radius: calc(0.25rem - 1px);--bs-dropdown-divider-bg: rgba(0, 0, 0, 0.175);--bs-dropdown-divider-margin-y: 0.5rem;--bs-dropdown-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-dropdown-link-color: #343a40;--bs-dropdown-link-hover-color: #343a40;--bs-dropdown-link-hover-bg: #f8f9fa;--bs-dropdown-link-active-color: #fff;--bs-dropdown-link-active-bg: #2780e3;--bs-dropdown-link-disabled-color: rgba(52, 58, 64, 0.5);--bs-dropdown-item-padding-x: 1rem;--bs-dropdown-item-padding-y: 0.25rem;--bs-dropdown-header-color: #6c757d;--bs-dropdown-header-padding-x: 1rem;--bs-dropdown-header-padding-y: 0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.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:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid rgba(0,0,0,0);border-bottom:.3em solid;border-left:.3em solid rgba(0,0,0,0)}.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:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid rgba(0,0,0,0);border-right:0;border-bottom:.3em solid rgba(0,0,0,0);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:var(--bs-dropdown-spacer)}.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 rgba(0,0,0,0);border-right:.3em solid;border-bottom:.3em solid rgba(0,0,0,0)}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);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:rgba(0,0,0,0);border:0}.dropdown-item:hover,.dropdown-item:focus{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:rgba(0,0,0,0)}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:0.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color: #dee2e6;--bs-dropdown-bg: #343a40;--bs-dropdown-border-color: rgba(0, 0, 0, 0.175);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color: #dee2e6;--bs-dropdown-link-hover-color: #fff;--bs-dropdown-divider-bg: rgba(0, 0, 0, 0.175);--bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color: #fff;--bs-dropdown-link-active-bg: #2780e3;--bs-dropdown-link-disabled-color: #adb5bd;--bs-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>:not(.btn-check:first-child)+.btn,.btn-group>.btn-group:not(:first-child){margin-left:calc(1px*-1)}.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:calc(1px*-1)}.nav{--bs-nav-link-padding-x: 1rem;--bs-nav-link-padding-y: 0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color: #2761e3;--bs-nav-link-hover-color: #1f4eb6;--bs-nav-link-disabled-color: rgba(52, 58, 64, 0.75);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:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;background:none;border:0;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:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(39,128,227,.25)}.nav-link.disabled,.nav-link:disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width: 1px;--bs-nav-tabs-border-color: #dee2e6;--bs-nav-tabs-border-radius: 0.25rem;--bs-nav-tabs-link-hover-border-color: #e9ecef #e9ecef #dee2e6;--bs-nav-tabs-link-active-color: #000;--bs-nav-tabs-link-active-bg: #fff;--bs-nav-tabs-link-active-border-color: #dee2e6 #dee2e6 #fff;border-bottom:var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1*var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width) solid rgba(0,0,0,0)}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1*var(--bs-nav-tabs-border-width))}.nav-pills{--bs-nav-pills-border-radius: 0.25rem;--bs-nav-pills-link-active-color: #fff;--bs-nav-pills-link-active-bg: #2780e3}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-underline{--bs-nav-underline-gap: 1rem;--bs-nav-underline-border-width: 0.125rem;--bs-nav-underline-link-active-color: #000;gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width) solid rgba(0,0,0,0)}.nav-underline .nav-link:hover,.nav-underline .nav-link:focus{border-bottom-color:currentcolor}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:currentcolor}.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{--bs-navbar-padding-x: 0;--bs-navbar-padding-y: 0.5rem;--bs-navbar-color: #fdfeff;--bs-navbar-hover-color: rgba(253, 253, 255, 0.8);--bs-navbar-disabled-color: rgba(253, 254, 255, 0.75);--bs-navbar-active-color: #fdfdff;--bs-navbar-brand-padding-y: 0.3125rem;--bs-navbar-brand-margin-end: 1rem;--bs-navbar-brand-font-size: 1.25rem;--bs-navbar-brand-color: #fdfeff;--bs-navbar-brand-hover-color: #fdfdff;--bs-navbar-nav-link-padding-x: 0.5rem;--bs-navbar-toggler-padding-y: 0.25;--bs-navbar-toggler-padding-x: 0;--bs-navbar-toggler-font-size: 1.25rem;--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23fdfeff' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color: rgba(253, 254, 255, 0);--bs-navbar-toggler-border-radius: 0.25rem;--bs-navbar-toggler-focus-width: 0.25rem;--bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out;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:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-sm,.navbar>.container-md,.navbar>.container-lg,.navbar>.container-xl,.navbar>.container-xxl{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:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x: 0;--bs-nav-link-padding-y: 0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color: var(--bs-navbar-color);--bs-nav-link-hover-color: var(--bs-navbar-hover-color);--bs-nav-link-disabled-color: var(--bs-navbar-disabled-color);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.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:hover,.navbar-text a:focus{color:var(--bs-navbar-active-color)}.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:var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:rgba(0,0,0,0);border:var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);transition:var(--bs-navbar-toggler-transition)}@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 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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{position:static;z-index:auto;flex-grow:1;-webkit-flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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{position:static;z-index:auto;flex-grow:1;-webkit-flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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{position:static;z-index:auto;flex-grow:1;-webkit-flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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{position:static;z-index:auto;flex-grow:1;-webkit-flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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{position:static;z-index:auto;flex-grow:1;-webkit-flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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{position:static;z-index:auto;flex-grow:1;-webkit-flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark]{--bs-navbar-color: #fdfeff;--bs-navbar-hover-color: rgba(253, 253, 255, 0.8);--bs-navbar-disabled-color: rgba(253, 254, 255, 0.75);--bs-navbar-active-color: #fdfdff;--bs-navbar-brand-color: #fdfeff;--bs-navbar-brand-hover-color: #fdfdff;--bs-navbar-toggler-border-color: rgba(253, 254, 255, 0);--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23fdfeff' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23fdfeff' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y: 1rem;--bs-card-spacer-x: 1rem;--bs-card-title-spacer-y: 0.5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width: 1px;--bs-card-border-color: rgba(0, 0, 0, 0.175);--bs-card-border-radius: 0.25rem;--bs-card-box-shadow: ;--bs-card-inner-border-radius: calc(0.25rem - 1px);--bs-card-cap-padding-y: 0.5rem;--bs-card-cap-padding-x: 1rem;--bs-card-cap-bg: rgba(52, 58, 64, 0.25);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg: #fff;--bs-card-img-overlay-padding: 1rem;--bs-card-group-margin: 0.75rem;position:relative;display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0}.card>.list-group:last-child{border-bottom-width:0}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;-webkit-flex:1 1 auto;padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-0.5*var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-footer{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-header-tabs{margin-right:calc(-0.5*var(--bs-card-cap-padding-x));margin-bottom:calc(-1*var(--bs-card-cap-padding-y));margin-left:calc(-0.5*var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-0.5*var(--bs-card-cap-padding-x));margin-left:calc(-0.5*var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding)}.card-img,.card-img-top,.card-img-bottom{width:100%}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@media(min-width: 576px){.card-group{display:flex;display:-webkit-flex;flex-flow:row wrap;-webkit-flex-flow:row wrap}.card-group>.card{flex:1 0 0%;-webkit-flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}}.accordion{--bs-accordion-color: #343a40;--bs-accordion-bg: #fff;--bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease;--bs-accordion-border-color: #dee2e6;--bs-accordion-border-width: 1px;--bs-accordion-border-radius: 0.25rem;--bs-accordion-inner-border-radius: calc(0.25rem - 1px);--bs-accordion-btn-padding-x: 1.25rem;--bs-accordion-btn-padding-y: 1rem;--bs-accordion-btn-color: #343a40;--bs-accordion-btn-bg: #fff;--bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23343a40'%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");--bs-accordion-btn-icon-width: 1.25rem;--bs-accordion-btn-icon-transform: rotate(-180deg);--bs-accordion-btn-icon-transition: transform 0.2s ease-in-out;--bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2310335b'%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");--bs-accordion-btn-focus-border-color: #93c0f1;--bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(39, 128, 227, 0.25);--bs-accordion-body-padding-x: 1.25rem;--bs-accordion-body-padding-y: 1rem;--bs-accordion-active-color: #10335b;--bs-accordion-active-bg: #d4e6f9}.accordion-button{position:relative;display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media(prefers-reduced-motion: reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1*var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;-webkit-flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media(prefers-reduced-motion: reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:var(--bs-accordion-btn-focus-border-color);outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.accordion-item:not(:first-of-type){border-top:0}.accordion-body{padding:var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x)}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%237db3ee'%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");--bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%237db3ee'%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")}.breadcrumb{--bs-breadcrumb-padding-x: 0;--bs-breadcrumb-padding-y: 0;--bs-breadcrumb-margin-bottom: 1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color: rgba(52, 58, 64, 0.75);--bs-breadcrumb-item-padding-x: 0.5rem;--bs-breadcrumb-item-active-color: rgba(52, 58, 64, 0.75);display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider, ">") /* rtl: var(--bs-breadcrumb-divider, ">") */}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x: 0.75rem;--bs-pagination-padding-y: 0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color: #2761e3;--bs-pagination-bg: #fff;--bs-pagination-border-width: 1px;--bs-pagination-border-color: #dee2e6;--bs-pagination-border-radius: 0.25rem;--bs-pagination-hover-color: #1f4eb6;--bs-pagination-hover-bg: #f8f9fa;--bs-pagination-hover-border-color: #dee2e6;--bs-pagination-focus-color: #1f4eb6;--bs-pagination-focus-bg: #e9ecef;--bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(39, 128, 227, 0.25);--bs-pagination-active-color: #fff;--bs-pagination-active-bg: #2780e3;--bs-pagination-active-border-color: #2780e3;--bs-pagination-disabled-color: rgba(52, 58, 64, 0.75);--bs-pagination-disabled-bg: #e9ecef;--bs-pagination-disabled-border-color: #dee2e6;display:flex;display:-webkit-flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);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:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.page-link.active,.active>.page-link{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.page-link.disabled,.disabled>.page-link{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(1px*-1)}.pagination-lg{--bs-pagination-padding-x: 1.5rem;--bs-pagination-padding-y: 0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius: 0.5rem}.pagination-sm{--bs-pagination-padding-x: 0.5rem;--bs-pagination-padding-y: 0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius: 0.2em}.badge{--bs-badge-padding-x: 0.65em;--bs-badge-padding-y: 0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight: 700;--bs-badge-color: #fff;--bs-badge-border-radius: 0.25rem;display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg: transparent;--bs-alert-padding-x: 1rem;--bs-alert-padding-y: 1rem;--bs-alert-margin-bottom: 1rem;--bs-alert-color: inherit;--bs-alert-border-color: transparent;--bs-alert-border: 0 solid var(--bs-alert-border-color);--bs-alert-border-radius: 0.25rem;--bs-alert-link-color: inherit;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-default{--bs-alert-color: var(--bs-default-text-emphasis);--bs-alert-bg: var(--bs-default-bg-subtle);--bs-alert-border-color: var(--bs-default-border-subtle);--bs-alert-link-color: var(--bs-default-text-emphasis)}.alert-primary{--bs-alert-color: var(--bs-primary-text-emphasis);--bs-alert-bg: var(--bs-primary-bg-subtle);--bs-alert-border-color: var(--bs-primary-border-subtle);--bs-alert-link-color: var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color: var(--bs-secondary-text-emphasis);--bs-alert-bg: var(--bs-secondary-bg-subtle);--bs-alert-border-color: var(--bs-secondary-border-subtle);--bs-alert-link-color: var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color: var(--bs-success-text-emphasis);--bs-alert-bg: var(--bs-success-bg-subtle);--bs-alert-border-color: var(--bs-success-border-subtle);--bs-alert-link-color: var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color: var(--bs-info-text-emphasis);--bs-alert-bg: var(--bs-info-bg-subtle);--bs-alert-border-color: var(--bs-info-border-subtle);--bs-alert-link-color: var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color: var(--bs-warning-text-emphasis);--bs-alert-bg: var(--bs-warning-bg-subtle);--bs-alert-border-color: var(--bs-warning-border-subtle);--bs-alert-link-color: var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color: var(--bs-danger-text-emphasis);--bs-alert-bg: var(--bs-danger-bg-subtle);--bs-alert-border-color: var(--bs-danger-border-subtle);--bs-alert-link-color: var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color: var(--bs-light-text-emphasis);--bs-alert-bg: var(--bs-light-bg-subtle);--bs-alert-border-color: var(--bs-light-border-subtle);--bs-alert-link-color: var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color: var(--bs-dark-text-emphasis);--bs-alert-bg: var(--bs-dark-bg-subtle);--bs-alert-border-color: var(--bs-dark-border-subtle);--bs-alert-link-color: var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:.5rem}}.progress,.progress-stacked{--bs-progress-height: 0.5rem;--bs-progress-font-size:0.75rem;--bs-progress-bg: #e9ecef;--bs-progress-border-radius: 0.25rem;--bs-progress-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-progress-bar-color: #fff;--bs-progress-bar-bg: #2780e3;--bs-progress-bar-transition: width 0.6s ease;display:flex;display:-webkit-flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg)}.progress-bar{display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;justify-content:center;-webkit-justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@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:var(--bs-progress-height) var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media(prefers-reduced-motion: reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color: #343a40;--bs-list-group-bg: #fff;--bs-list-group-border-color: #dee2e6;--bs-list-group-border-width: 1px;--bs-list-group-border-radius: 0.25rem;--bs-list-group-item-padding-x: 1rem;--bs-list-group-item-padding-y: 0.5rem;--bs-list-group-action-color: rgba(52, 58, 64, 0.75);--bs-list-group-action-hover-color: #000;--bs-list-group-action-hover-bg: #f8f9fa;--bs-list-group-action-active-color: #343a40;--bs-list-group-action-active-bg: #e9ecef;--bs-list-group-disabled-color: rgba(52, 58, 64, 0.75);--bs-list-group-disabled-bg: #fff;--bs-list-group-active-color: #fff;--bs-list-group-active-bg: #2780e3;--bs-list-group-active-border-color: #2780e3;display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;padding-left:0;margin-bottom:0}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:hover,.list-group-item-action:focus{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color)}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1*var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-horizontal{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media(min-width: 576px){.list-group-horizontal-sm{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width: 768px){.list-group-horizontal-md{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width: 992px){.list-group-horizontal-lg{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width: 1200px){.list-group-horizontal-xl{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width: 1400px){.list-group-horizontal-xxl{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-default{--bs-list-group-color: var(--bs-default-text-emphasis);--bs-list-group-bg: var(--bs-default-bg-subtle);--bs-list-group-border-color: var(--bs-default-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-default-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-default-border-subtle);--bs-list-group-active-color: var(--bs-default-bg-subtle);--bs-list-group-active-bg: var(--bs-default-text-emphasis);--bs-list-group-active-border-color: var(--bs-default-text-emphasis)}.list-group-item-primary{--bs-list-group-color: var(--bs-primary-text-emphasis);--bs-list-group-bg: var(--bs-primary-bg-subtle);--bs-list-group-border-color: var(--bs-primary-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-primary-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-primary-border-subtle);--bs-list-group-active-color: var(--bs-primary-bg-subtle);--bs-list-group-active-bg: var(--bs-primary-text-emphasis);--bs-list-group-active-border-color: var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color: var(--bs-secondary-text-emphasis);--bs-list-group-bg: var(--bs-secondary-bg-subtle);--bs-list-group-border-color: var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-secondary-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-secondary-border-subtle);--bs-list-group-active-color: var(--bs-secondary-bg-subtle);--bs-list-group-active-bg: var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color: var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color: var(--bs-success-text-emphasis);--bs-list-group-bg: var(--bs-success-bg-subtle);--bs-list-group-border-color: var(--bs-success-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-success-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-success-border-subtle);--bs-list-group-active-color: var(--bs-success-bg-subtle);--bs-list-group-active-bg: var(--bs-success-text-emphasis);--bs-list-group-active-border-color: var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color: var(--bs-info-text-emphasis);--bs-list-group-bg: var(--bs-info-bg-subtle);--bs-list-group-border-color: var(--bs-info-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-info-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-info-border-subtle);--bs-list-group-active-color: var(--bs-info-bg-subtle);--bs-list-group-active-bg: var(--bs-info-text-emphasis);--bs-list-group-active-border-color: var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color: var(--bs-warning-text-emphasis);--bs-list-group-bg: var(--bs-warning-bg-subtle);--bs-list-group-border-color: var(--bs-warning-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-warning-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-warning-border-subtle);--bs-list-group-active-color: var(--bs-warning-bg-subtle);--bs-list-group-active-bg: var(--bs-warning-text-emphasis);--bs-list-group-active-border-color: var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color: var(--bs-danger-text-emphasis);--bs-list-group-bg: var(--bs-danger-bg-subtle);--bs-list-group-border-color: var(--bs-danger-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-danger-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-danger-border-subtle);--bs-list-group-active-color: var(--bs-danger-bg-subtle);--bs-list-group-active-bg: var(--bs-danger-text-emphasis);--bs-list-group-active-border-color: var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color: var(--bs-light-text-emphasis);--bs-list-group-bg: var(--bs-light-bg-subtle);--bs-list-group-border-color: var(--bs-light-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-light-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-light-border-subtle);--bs-list-group-active-color: var(--bs-light-bg-subtle);--bs-list-group-active-bg: var(--bs-light-text-emphasis);--bs-list-group-active-border-color: var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color: var(--bs-dark-text-emphasis);--bs-list-group-bg: var(--bs-dark-bg-subtle);--bs-list-group-border-color: var(--bs-dark-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-dark-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-dark-border-subtle);--bs-list-group-active-color: var(--bs-dark-bg-subtle);--bs-list-group-active-bg: var(--bs-dark-text-emphasis);--bs-list-group-active-border-color: var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color: #000;--bs-btn-close-bg: 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 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");--bs-btn-close-opacity: 0.5;--bs-btn-close-hover-opacity: 0.75;--bs-btn-close-focus-shadow: 0 0 0 0.25rem rgba(39, 128, 227, 0.25);--bs-btn-close-focus-opacity: 1;--bs-btn-close-disabled-opacity: 0.25;--bs-btn-close-white-filter: invert(1) grayscale(100%) brightness(200%);box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:var(--bs-btn-close-color);background:rgba(0,0,0,0) var(--bs-btn-close-bg) center/1em auto no-repeat;border:0;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.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:var(--bs-btn-close-disabled-opacity)}.btn-close-white{filter:var(--bs-btn-close-white-filter)}[data-bs-theme=dark] .btn-close{filter:var(--bs-btn-close-white-filter)}.toast{--bs-toast-zindex: 1090;--bs-toast-padding-x: 0.75rem;--bs-toast-padding-y: 0.5rem;--bs-toast-spacing: 1.5rem;--bs-toast-max-width: 350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg: rgba(255, 255, 255, 0.85);--bs-toast-border-width: 1px;--bs-toast-border-color: rgba(0, 0, 0, 0.175);--bs-toast-border-radius: 0.25rem;--bs-toast-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-toast-header-color: rgba(52, 58, 64, 0.75);--bs-toast-header-bg: rgba(255, 255, 255, 0.85);--bs-toast-header-border-color: rgba(0, 0, 0, 0.175);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width) solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex: 1090;position:absolute;z-index:var(--bs-toast-zindex);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:var(--bs-toast-spacing)}.toast-header{display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;padding:var(--bs-toast-padding-y) var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width) solid var(--bs-toast-header-border-color)}.toast-header .btn-close{margin-right:calc(-0.5*var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex: 1055;--bs-modal-width: 500px;--bs-modal-padding: 1rem;--bs-modal-margin: 0.5rem;--bs-modal-color: ;--bs-modal-bg: #fff;--bs-modal-border-color: rgba(0, 0, 0, 0.175);--bs-modal-border-width: 1px;--bs-modal-border-radius: 0.5rem;--bs-modal-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-modal-inner-border-radius: calc(0.5rem - 1px);--bs-modal-header-padding-x: 1rem;--bs-modal-header-padding-y: 1rem;--bs-modal-header-padding: 1rem 1rem;--bs-modal-header-border-color: #dee2e6;--bs-modal-header-border-width: 1px;--bs-modal-title-line-height: 1.5;--bs-modal-footer-gap: 0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color: #dee2e6;--bs-modal-footer-border-width: 1px;position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);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% - var(--bs-modal-margin)*2)}.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% - var(--bs-modal-margin)*2)}.modal-content{position:relative;display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width) solid var(--bs-modal-border-color);outline:0}.modal-backdrop{--bs-backdrop-zindex: 1050;--bs-backdrop-bg: #000;--bs-backdrop-opacity: 0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.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:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y)*.5) calc(var(--bs-modal-header-padding-x)*.5);margin:calc(-0.5*var(--bs-modal-header-padding-y)) calc(-0.5*var(--bs-modal-header-padding-x)) calc(-0.5*var(--bs-modal-header-padding-y)) auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;-webkit-flex:1 1 auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;display:-webkit-flex;flex-shrink:0;-webkit-flex-shrink:0;flex-wrap:wrap;-webkit-flex-wrap:wrap;align-items:center;-webkit-align-items:center;justify-content:flex-end;-webkit-justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap)*.5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap)*.5)}@media(min-width: 576px){.modal{--bs-modal-margin: 1.75rem;--bs-modal-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width: 300px}}@media(min-width: 992px){.modal-lg,.modal-xl{--bs-modal-width: 800px}}@media(min-width: 1200px){.modal-xl{--bs-modal-width: 1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0}.modal-fullscreen .modal-body{overflow-y:auto}@media(max-width: 575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media(max-width: 767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media(max-width: 991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media(max-width: 1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media(max-width: 1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex: 1080;--bs-tooltip-max-width: 200px;--bs-tooltip-padding-x: 0.5rem;--bs-tooltip-padding-y: 0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color: #fff;--bs-tooltip-bg: #000;--bs-tooltip-border-radius: 0.25rem;--bs-tooltip-opacity: 0.9;--bs-tooltip-arrow-width: 0.8rem;--bs-tooltip-arrow-height: 0.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);font-family:"Source Sans Pro",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";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;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:rgba(0,0,0,0);border-style:solid}.bs-tooltip-top .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow{bottom:calc(-1*var(--bs-tooltip-arrow-height))}.bs-tooltip-top .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width)*.5) 0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-end .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow{left:calc(-1*var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-end .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width)*.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width)*.5) 0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-bottom .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow{top:calc(-1*var(--bs-tooltip-arrow-height))}.bs-tooltip-bottom .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width)*.5) var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-start .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow{right:calc(-1*var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-start .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width)*.5) 0 calc(var(--bs-tooltip-arrow-width)*.5) var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg)}.popover{--bs-popover-zindex: 1070;--bs-popover-max-width: 276px;--bs-popover-font-size:0.875rem;--bs-popover-bg: #fff;--bs-popover-border-width: 1px;--bs-popover-border-color: rgba(0, 0, 0, 0.175);--bs-popover-border-radius: 0.5rem;--bs-popover-inner-border-radius: calc(0.5rem - 1px);--bs-popover-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-popover-header-padding-x: 1rem;--bs-popover-header-padding-y: 0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color: inherit;--bs-popover-header-bg: #e9ecef;--bs-popover-body-padding-x: 1rem;--bs-popover-body-padding-y: 1rem;--bs-popover-body-color: #343a40;--bs-popover-arrow-width: 1rem;--bs-popover-arrow-height: 0.5rem;--bs-popover-arrow-border: var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:"Source Sans Pro",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";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;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width) solid var(--bs-popover-border-color)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::before,.popover .popover-arrow::after{position:absolute;display:block;content:"";border-color:rgba(0,0,0,0);border-style:solid;border-width:0}.bs-popover-top>.popover-arrow,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow{bottom:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-top>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after{border-width:var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width)*.5) 0}.bs-popover-top>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-top>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-end>.popover-arrow,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow{left:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-end>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after{border-width:calc(var(--bs-popover-arrow-width)*.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width)*.5) 0}.bs-popover-end>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-end>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-bottom>.popover-arrow,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow{top:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-bottom>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after{border-width:0 calc(var(--bs-popover-arrow-width)*.5) var(--bs-popover-arrow-height)}.bs-popover-bottom>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-bottom>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.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:var(--bs-popover-arrow-width);margin-left:calc(-0.5*var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-header-bg)}.bs-popover-start>.popover-arrow,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow{right:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-start>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after{border-width:calc(var(--bs-popover-arrow-width)*.5) 0 calc(var(--bs-popover-arrow-width)*.5) var(--bs-popover-arrow-height)}.bs-popover-start>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-start>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-border-color)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.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%}.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 rgba(0,0,0,0);border-bottom:10px solid rgba(0,0,0,0);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}[data-bs-theme=dark] .carousel .carousel-control-prev-icon,[data-bs-theme=dark] .carousel .carousel-control-next-icon,[data-bs-theme=dark].carousel .carousel-control-prev-icon,[data-bs-theme=dark].carousel .carousel-control-next-icon{filter:invert(1) grayscale(100)}[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target],[data-bs-theme=dark].carousel .carousel-indicators [data-bs-target]{background-color:#000}[data-bs-theme=dark] .carousel .carousel-caption,[data-bs-theme=dark].carousel .carousel-caption{color:#000}.spinner-grow,.spinner-border{display:inline-block;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg) /* rtl:ignore */}}.spinner-border{--bs-spinner-width: 2rem;--bs-spinner-height: 2rem;--bs-spinner-vertical-align: -0.125em;--bs-spinner-border-width: 0.25em;--bs-spinner-animation-speed: 0.75s;--bs-spinner-animation-name: spinner-border;border:var(--bs-spinner-border-width) solid currentcolor;border-right-color:rgba(0,0,0,0)}.spinner-border-sm{--bs-spinner-width: 1rem;--bs-spinner-height: 1rem;--bs-spinner-border-width: 0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width: 2rem;--bs-spinner-height: 2rem;--bs-spinner-vertical-align: -0.125em;--bs-spinner-animation-speed: 0.75s;--bs-spinner-animation-name: spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width: 1rem;--bs-spinner-height: 1rem}@media(prefers-reduced-motion: reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed: 1.5s}}.offcanvas,.offcanvas-xxl,.offcanvas-xl,.offcanvas-lg,.offcanvas-md,.offcanvas-sm{--bs-offcanvas-zindex: 1045;--bs-offcanvas-width: 400px;--bs-offcanvas-height: 30vh;--bs-offcanvas-padding-x: 1rem;--bs-offcanvas-padding-y: 1rem;--bs-offcanvas-color: #343a40;--bs-offcanvas-bg: #fff;--bs-offcanvas-border-width: 1px;--bs-offcanvas-border-color: rgba(0, 0, 0, 0.175);--bs-offcanvas-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-offcanvas-transition: transform 0.3s ease-in-out;--bs-offcanvas-title-line-height: 1.5}@media(max-width: 575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 575.98px)and (prefers-reduced-motion: reduce){.offcanvas-sm{transition:none}}@media(max-width: 575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.showing,.offcanvas-sm.show:not(.hiding){transform:none}.offcanvas-sm.showing,.offcanvas-sm.hiding,.offcanvas-sm.show{visibility:visible}}@media(min-width: 576px){.offcanvas-sm{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}@media(max-width: 767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 767.98px)and (prefers-reduced-motion: reduce){.offcanvas-md{transition:none}}@media(max-width: 767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.showing,.offcanvas-md.show:not(.hiding){transform:none}.offcanvas-md.showing,.offcanvas-md.hiding,.offcanvas-md.show{visibility:visible}}@media(min-width: 768px){.offcanvas-md{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}@media(max-width: 991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 991.98px)and (prefers-reduced-motion: reduce){.offcanvas-lg{transition:none}}@media(max-width: 991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.showing,.offcanvas-lg.show:not(.hiding){transform:none}.offcanvas-lg.showing,.offcanvas-lg.hiding,.offcanvas-lg.show{visibility:visible}}@media(min-width: 992px){.offcanvas-lg{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}@media(max-width: 1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 1199.98px)and (prefers-reduced-motion: reduce){.offcanvas-xl{transition:none}}@media(max-width: 1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.showing,.offcanvas-xl.show:not(.hiding){transform:none}.offcanvas-xl.showing,.offcanvas-xl.hiding,.offcanvas-xl.show{visibility:visible}}@media(min-width: 1200px){.offcanvas-xl{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}@media(max-width: 1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 1399.98px)and (prefers-reduced-motion: reduce){.offcanvas-xxl{transition:none}}@media(max-width: 1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.showing,.offcanvas-xxl.show:not(.hiding){transform:none}.offcanvas-xxl.showing,.offcanvas-xxl.hiding,.offcanvas-xxl.show{visibility:visible}}@media(min-width: 1400px){.offcanvas-xxl{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media(prefers-reduced-motion: reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.showing,.offcanvas.show:not(.hiding){transform:none}.offcanvas.showing,.offcanvas.hiding,.offcanvas.show{visibility:visible}.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:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y)*.5) calc(var(--bs-offcanvas-padding-x)*.5);margin-top:calc(-0.5*var(--bs-offcanvas-padding-y));margin-right:calc(-0.5*var(--bs-offcanvas-padding-x));margin-bottom:calc(-0.5*var(--bs-offcanvas-padding-y))}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;-webkit-flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto}.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:""}.text-bg-default{color:#fff !important;background-color:RGBA(var(--bs-default-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-primary{color:#fff !important;background-color:RGBA(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-secondary{color:#fff !important;background-color:RGBA(var(--bs-secondary-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-success{color:#fff !important;background-color:RGBA(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-info{color:#fff !important;background-color:RGBA(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-warning{color:#fff !important;background-color:RGBA(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-danger{color:#fff !important;background-color:RGBA(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-light{color:#000 !important;background-color:RGBA(var(--bs-light-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-dark{color:#fff !important;background-color:RGBA(var(--bs-dark-rgb), var(--bs-bg-opacity, 1)) !important}.link-default{color:RGBA(var(--bs-default-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-default-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-default:hover,.link-default:focus{color:RGBA(42, 46, 51, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(42, 46, 51, var(--bs-link-underline-opacity, 1)) !important}.link-primary{color:RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-primary:hover,.link-primary:focus{color:RGBA(31, 102, 182, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(31, 102, 182, var(--bs-link-underline-opacity, 1)) !important}.link-secondary{color:RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-secondary:hover,.link-secondary:focus{color:RGBA(42, 46, 51, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(42, 46, 51, var(--bs-link-underline-opacity, 1)) !important}.link-success{color:RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-success:hover,.link-success:focus{color:RGBA(50, 146, 19, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(50, 146, 19, var(--bs-link-underline-opacity, 1)) !important}.link-info{color:RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-info:hover,.link-info:focus{color:RGBA(122, 67, 150, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(122, 67, 150, var(--bs-link-underline-opacity, 1)) !important}.link-warning{color:RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-warning:hover,.link-warning:focus{color:RGBA(204, 94, 19, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(204, 94, 19, var(--bs-link-underline-opacity, 1)) !important}.link-danger{color:RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-danger:hover,.link-danger:focus{color:RGBA(204, 0, 46, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(204, 0, 46, var(--bs-link-underline-opacity, 1)) !important}.link-light{color:RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-light:hover,.link-light:focus{color:RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important}.link-dark{color:RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-dark:hover,.link-dark:focus{color:RGBA(42, 46, 51, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(42, 46, 51, var(--bs-link-underline-opacity, 1)) !important}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-body-emphasis:hover,.link-body-emphasis:focus{color:RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75)) !important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;-webkit-align-items:center;text-decoration-color:rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5));text-underline-offset:.25em;backface-visibility:hidden;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;-o-backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;-webkit-flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media(prefers-reduced-motion: reduce){.icon-link>.bi{transition:none}}.icon-link-hover:hover>.bi,.icon-link-hover:focus-visible>.bi{transform:var(--bs-icon-link-transform, translate3d(0.25em, 0, 0))}.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: 75%}.ratio-16x9{--bs-aspect-ratio: 56.25%}.ratio-21x9{--bs-aspect-ratio: 42.8571428571%}.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}.sticky-bottom{position:sticky;bottom:0;z-index:1020}@media(min-width: 576px){.sticky-sm-top{position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width: 768px){.sticky-md-top{position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width: 992px){.sticky-lg-top{position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width: 1200px){.sticky-xl-top{position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width: 1400px){.sticky-xxl-top{position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:sticky;bottom: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){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}.visually-hidden:not(caption),.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption){position:absolute !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}.object-fit-contain{object-fit:contain !important}.object-fit-cover{object-fit:cover !important}.object-fit-fill{object-fit:fill !important}.object-fit-scale{object-fit:scale-down !important}.object-fit-none{object-fit: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}.overflow-x-auto{overflow-x:auto !important}.overflow-x-hidden{overflow-x:hidden !important}.overflow-x-visible{overflow-x:visible !important}.overflow-x-scroll{overflow-x:scroll !important}.overflow-y-auto{overflow-y:auto !important}.overflow-y-hidden{overflow-y:hidden !important}.overflow-y-visible{overflow-y:visible !important}.overflow-y-scroll{overflow-y: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-inline-grid{display:inline-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}.focus-ring-default{--bs-focus-ring-color: rgba(var(--bs-default-rgb), var(--bs-focus-ring-opacity))}.focus-ring-primary{--bs-focus-ring-color: rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color: rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color: rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color: rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color: rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color: rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.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:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-0{border:0 !important}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-top-0{border-top:0 !important}.border-end{border-right:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-end-0{border-right:0 !important}.border-bottom{border-bottom:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-bottom-0{border-bottom:0 !important}.border-start{border-left:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-start-0{border-left:0 !important}.border-default{--bs-border-opacity: 1;border-color:rgba(var(--bs-default-rgb), var(--bs-border-opacity)) !important}.border-primary{--bs-border-opacity: 1;border-color:rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important}.border-secondary{--bs-border-opacity: 1;border-color:rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important}.border-success{--bs-border-opacity: 1;border-color:rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important}.border-info{--bs-border-opacity: 1;border-color:rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important}.border-warning{--bs-border-opacity: 1;border-color:rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important}.border-danger{--bs-border-opacity: 1;border-color:rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important}.border-light{--bs-border-opacity: 1;border-color:rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important}.border-dark{--bs-border-opacity: 1;border-color:rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important}.border-black{--bs-border-opacity: 1;border-color:rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important}.border-white{--bs-border-opacity: 1;border-color:rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle) !important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle) !important}.border-success-subtle{border-color:var(--bs-success-border-subtle) !important}.border-info-subtle{border-color:var(--bs-info-border-subtle) !important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle) !important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle) !important}.border-light-subtle{border-color:var(--bs-light-border-subtle) !important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle) !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}.border-opacity-10{--bs-border-opacity: 0.1}.border-opacity-25{--bs-border-opacity: 0.25}.border-opacity-50{--bs-border-opacity: 0.5}.border-opacity-75{--bs-border-opacity: 0.75}.border-opacity-100{--bs-border-opacity: 1}.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}.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}.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}.row-gap-0{row-gap:0 !important}.row-gap-1{row-gap:.25rem !important}.row-gap-2{row-gap:.5rem !important}.row-gap-3{row-gap:1rem !important}.row-gap-4{row-gap:1.5rem !important}.row-gap-5{row-gap:3rem !important}.column-gap-0{column-gap:0 !important}.column-gap-1{column-gap:.25rem !important}.column-gap-2{column-gap:.5rem !important}.column-gap-3{column-gap:1rem !important}.column-gap-4{column-gap:1.5rem !important}.column-gap-5{column-gap:3rem !important}.font-monospace{font-family:var(--bs-font-monospace) !important}.fs-1{font-size:calc(1.325rem + 0.9vw) !important}.fs-2{font-size:calc(1.29rem + 0.48vw) !important}.fs-3{font-size:calc(1.27rem + 0.24vw) !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-lighter{font-weight:lighter !important}.fw-light{font-weight:300 !important}.fw-normal{font-weight:400 !important}.fw-medium{font-weight:500 !important}.fw-semibold{font-weight:600 !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:var(--bs-secondary-color) !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-body-secondary{--bs-text-opacity: 1;color:var(--bs-secondary-color) !important}.text-body-tertiary{--bs-text-opacity: 1;color:var(--bs-tertiary-color) !important}.text-body-emphasis{--bs-text-opacity: 1;color:var(--bs-emphasis-color) !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}.text-primary-emphasis{color:var(--bs-primary-text-emphasis) !important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis) !important}.text-success-emphasis{color:var(--bs-success-text-emphasis) !important}.text-info-emphasis{color:var(--bs-info-text-emphasis) !important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis) !important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis) !important}.text-light-emphasis{color:var(--bs-light-text-emphasis) !important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis) !important}.link-opacity-10{--bs-link-opacity: 0.1}.link-opacity-10-hover:hover{--bs-link-opacity: 0.1}.link-opacity-25{--bs-link-opacity: 0.25}.link-opacity-25-hover:hover{--bs-link-opacity: 0.25}.link-opacity-50{--bs-link-opacity: 0.5}.link-opacity-50-hover:hover{--bs-link-opacity: 0.5}.link-opacity-75{--bs-link-opacity: 0.75}.link-opacity-75-hover:hover{--bs-link-opacity: 0.75}.link-opacity-100{--bs-link-opacity: 1}.link-opacity-100-hover:hover{--bs-link-opacity: 1}.link-offset-1{text-underline-offset:.125em !important}.link-offset-1-hover:hover{text-underline-offset:.125em !important}.link-offset-2{text-underline-offset:.25em !important}.link-offset-2-hover:hover{text-underline-offset:.25em !important}.link-offset-3{text-underline-offset:.375em !important}.link-offset-3-hover:hover{text-underline-offset:.375em !important}.link-underline-default{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-default-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-primary{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-secondary{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-success{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-info{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-warning{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-danger{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-light{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-dark{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important}.link-underline{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-underline-opacity-0{--bs-link-underline-opacity: 0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity: 0}.link-underline-opacity-10{--bs-link-underline-opacity: 0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity: 0.1}.link-underline-opacity-25{--bs-link-underline-opacity: 0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity: 0.25}.link-underline-opacity-50{--bs-link-underline-opacity: 0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity: 0.5}.link-underline-opacity-75{--bs-link-underline-opacity: 0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity: 0.75}.link-underline-opacity-100{--bs-link-underline-opacity: 1}.link-underline-opacity-100-hover:hover{--bs-link-underline-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:rgba(0,0,0,0) !important}.bg-body-secondary{--bs-bg-opacity: 1;background-color:rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important}.bg-body-tertiary{--bs-bg-opacity: 1;background-color:rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !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-primary-subtle{background-color:var(--bs-primary-bg-subtle) !important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle) !important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle) !important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle) !important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle) !important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle) !important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle) !important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle) !important}.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:var(--bs-border-radius) !important}.rounded-0{border-radius:0 !important}.rounded-1{border-radius:var(--bs-border-radius-sm) !important}.rounded-2{border-radius:var(--bs-border-radius) !important}.rounded-3{border-radius:var(--bs-border-radius-lg) !important}.rounded-4{border-radius:var(--bs-border-radius-xl) !important}.rounded-5{border-radius:var(--bs-border-radius-xxl) !important}.rounded-circle{border-radius:50% !important}.rounded-pill{border-radius:var(--bs-border-radius-pill) !important}.rounded-top{border-top-left-radius:var(--bs-border-radius) !important;border-top-right-radius:var(--bs-border-radius) !important}.rounded-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm) !important;border-top-right-radius:var(--bs-border-radius-sm) !important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius) !important;border-top-right-radius:var(--bs-border-radius) !important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg) !important;border-top-right-radius:var(--bs-border-radius-lg) !important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl) !important;border-top-right-radius:var(--bs-border-radius-xl) !important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl) !important;border-top-right-radius:var(--bs-border-radius-xxl) !important}.rounded-top-circle{border-top-left-radius:50% !important;border-top-right-radius:50% !important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill) !important;border-top-right-radius:var(--bs-border-radius-pill) !important}.rounded-end{border-top-right-radius:var(--bs-border-radius) !important;border-bottom-right-radius:var(--bs-border-radius) !important}.rounded-end-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm) !important;border-bottom-right-radius:var(--bs-border-radius-sm) !important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius) !important;border-bottom-right-radius:var(--bs-border-radius) !important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg) !important;border-bottom-right-radius:var(--bs-border-radius-lg) !important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl) !important;border-bottom-right-radius:var(--bs-border-radius-xl) !important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl) !important;border-bottom-right-radius:var(--bs-border-radius-xxl) !important}.rounded-end-circle{border-top-right-radius:50% !important;border-bottom-right-radius:50% !important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill) !important;border-bottom-right-radius:var(--bs-border-radius-pill) !important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius) !important;border-bottom-left-radius:var(--bs-border-radius) !important}.rounded-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm) !important;border-bottom-left-radius:var(--bs-border-radius-sm) !important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius) !important;border-bottom-left-radius:var(--bs-border-radius) !important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg) !important;border-bottom-left-radius:var(--bs-border-radius-lg) !important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl) !important;border-bottom-left-radius:var(--bs-border-radius-xl) !important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl) !important;border-bottom-left-radius:var(--bs-border-radius-xxl) !important}.rounded-bottom-circle{border-bottom-right-radius:50% !important;border-bottom-left-radius:50% !important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill) !important;border-bottom-left-radius:var(--bs-border-radius-pill) !important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius) !important;border-top-left-radius:var(--bs-border-radius) !important}.rounded-start-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm) !important;border-top-left-radius:var(--bs-border-radius-sm) !important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius) !important;border-top-left-radius:var(--bs-border-radius) !important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg) !important;border-top-left-radius:var(--bs-border-radius-lg) !important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl) !important;border-top-left-radius:var(--bs-border-radius-xl) !important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl) !important;border-top-left-radius:var(--bs-border-radius-xxl) !important}.rounded-start-circle{border-bottom-left-radius:50% !important;border-top-left-radius:50% !important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill) !important;border-top-left-radius:var(--bs-border-radius-pill) !important}.visible{visibility:visible !important}.invisible{visibility:hidden !important}.z-n1{z-index:-1 !important}.z-0{z-index:0 !important}.z-1{z-index:1 !important}.z-2{z-index:2 !important}.z-3{z-index:3 !important}@media(min-width: 576px){.float-sm-start{float:left !important}.float-sm-end{float:right !important}.float-sm-none{float:none !important}.object-fit-sm-contain{object-fit:contain !important}.object-fit-sm-cover{object-fit:cover !important}.object-fit-sm-fill{object-fit:fill !important}.object-fit-sm-scale{object-fit:scale-down !important}.object-fit-sm-none{object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-sm-0{row-gap:0 !important}.row-gap-sm-1{row-gap:.25rem !important}.row-gap-sm-2{row-gap:.5rem !important}.row-gap-sm-3{row-gap:1rem !important}.row-gap-sm-4{row-gap:1.5rem !important}.row-gap-sm-5{row-gap:3rem !important}.column-gap-sm-0{column-gap:0 !important}.column-gap-sm-1{column-gap:.25rem !important}.column-gap-sm-2{column-gap:.5rem !important}.column-gap-sm-3{column-gap:1rem !important}.column-gap-sm-4{column-gap:1.5rem !important}.column-gap-sm-5{column-gap: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}.object-fit-md-contain{object-fit:contain !important}.object-fit-md-cover{object-fit:cover !important}.object-fit-md-fill{object-fit:fill !important}.object-fit-md-scale{object-fit:scale-down !important}.object-fit-md-none{object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-md-0{row-gap:0 !important}.row-gap-md-1{row-gap:.25rem !important}.row-gap-md-2{row-gap:.5rem !important}.row-gap-md-3{row-gap:1rem !important}.row-gap-md-4{row-gap:1.5rem !important}.row-gap-md-5{row-gap:3rem !important}.column-gap-md-0{column-gap:0 !important}.column-gap-md-1{column-gap:.25rem !important}.column-gap-md-2{column-gap:.5rem !important}.column-gap-md-3{column-gap:1rem !important}.column-gap-md-4{column-gap:1.5rem !important}.column-gap-md-5{column-gap: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}.object-fit-lg-contain{object-fit:contain !important}.object-fit-lg-cover{object-fit:cover !important}.object-fit-lg-fill{object-fit:fill !important}.object-fit-lg-scale{object-fit:scale-down !important}.object-fit-lg-none{object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-lg-0{row-gap:0 !important}.row-gap-lg-1{row-gap:.25rem !important}.row-gap-lg-2{row-gap:.5rem !important}.row-gap-lg-3{row-gap:1rem !important}.row-gap-lg-4{row-gap:1.5rem !important}.row-gap-lg-5{row-gap:3rem !important}.column-gap-lg-0{column-gap:0 !important}.column-gap-lg-1{column-gap:.25rem !important}.column-gap-lg-2{column-gap:.5rem !important}.column-gap-lg-3{column-gap:1rem !important}.column-gap-lg-4{column-gap:1.5rem !important}.column-gap-lg-5{column-gap: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}.object-fit-xl-contain{object-fit:contain !important}.object-fit-xl-cover{object-fit:cover !important}.object-fit-xl-fill{object-fit:fill !important}.object-fit-xl-scale{object-fit:scale-down !important}.object-fit-xl-none{object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-xl-0{row-gap:0 !important}.row-gap-xl-1{row-gap:.25rem !important}.row-gap-xl-2{row-gap:.5rem !important}.row-gap-xl-3{row-gap:1rem !important}.row-gap-xl-4{row-gap:1.5rem !important}.row-gap-xl-5{row-gap:3rem !important}.column-gap-xl-0{column-gap:0 !important}.column-gap-xl-1{column-gap:.25rem !important}.column-gap-xl-2{column-gap:.5rem !important}.column-gap-xl-3{column-gap:1rem !important}.column-gap-xl-4{column-gap:1.5rem !important}.column-gap-xl-5{column-gap: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}.object-fit-xxl-contain{object-fit:contain !important}.object-fit-xxl-cover{object-fit:cover !important}.object-fit-xxl-fill{object-fit:fill !important}.object-fit-xxl-scale{object-fit:scale-down !important}.object-fit-xxl-none{object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-xxl-0{row-gap:0 !important}.row-gap-xxl-1{row-gap:.25rem !important}.row-gap-xxl-2{row-gap:.5rem !important}.row-gap-xxl-3{row-gap:1rem !important}.row-gap-xxl-4{row-gap:1.5rem !important}.row-gap-xxl-5{row-gap:3rem !important}.column-gap-xxl-0{column-gap:0 !important}.column-gap-xxl-1{column-gap:.25rem !important}.column-gap-xxl-2{column-gap:.5rem !important}.column-gap-xxl-3{column-gap:1rem !important}.column-gap-xxl-4{column-gap:1.5rem !important}.column-gap-xxl-5{column-gap:3rem !important}.text-xxl-start{text-align:left !important}.text-xxl-end{text-align:right !important}.text-xxl-center{text-align:center !important}}.bg-default{color:#fff}.bg-primary{color:#fff}.bg-secondary{color:#fff}.bg-success{color:#fff}.bg-info{color:#fff}.bg-warning{color:#fff}.bg-danger{color:#fff}.bg-light{color:#000}.bg-dark{color:#fff}@media(min-width: 1200px){.fs-1{font-size:2rem !important}.fs-2{font-size:1.65rem !important}.fs-3{font-size:1.45rem !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-inline-grid{display:inline-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}}:root{--bslib-spacer: 1rem;--bslib-mb-spacer: var(--bslib-spacer, 1rem)}.bslib-mb-spacing{margin-bottom:var(--bslib-mb-spacer)}.bslib-gap-spacing{gap:var(--bslib-mb-spacer)}.bslib-gap-spacing>.bslib-mb-spacing,.bslib-gap-spacing>.form-group,.bslib-gap-spacing>p,.bslib-gap-spacing>pre{margin-bottom:0}.html-fill-container>.html-fill-item.bslib-mb-spacing{margin-bottom:0}.tab-content>.tab-pane.html-fill-container{display:none}.tab-content>.active.html-fill-container{display:flex}.tab-content.html-fill-container{padding:0}.bg-blue{--bslib-color-bg: #2780e3;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-blue{--bslib-color-fg: #2780e3;color:var(--bslib-color-fg)}.bg-indigo{--bslib-color-bg: #6610f2;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-indigo{--bslib-color-fg: #6610f2;color:var(--bslib-color-fg)}.bg-purple{--bslib-color-bg: #613d7c;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-purple{--bslib-color-fg: #613d7c;color:var(--bslib-color-fg)}.bg-pink{--bslib-color-bg: #e83e8c;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-pink{--bslib-color-fg: #e83e8c;color:var(--bslib-color-fg)}.bg-red{--bslib-color-bg: #ff0039;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-red{--bslib-color-fg: #ff0039;color:var(--bslib-color-fg)}.bg-orange{--bslib-color-bg: #f0ad4e;--bslib-color-fg: #000;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-orange{--bslib-color-fg: #f0ad4e;color:var(--bslib-color-fg)}.bg-yellow{--bslib-color-bg: #ff7518;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-yellow{--bslib-color-fg: #ff7518;color:var(--bslib-color-fg)}.bg-green{--bslib-color-bg: #3fb618;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-green{--bslib-color-fg: #3fb618;color:var(--bslib-color-fg)}.bg-teal{--bslib-color-bg: #20c997;--bslib-color-fg: #000;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-teal{--bslib-color-fg: #20c997;color:var(--bslib-color-fg)}.bg-cyan{--bslib-color-bg: #9954bb;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-cyan{--bslib-color-fg: #9954bb;color:var(--bslib-color-fg)}.text-default{--bslib-color-fg: #343a40}.bg-default{--bslib-color-bg: #343a40;--bslib-color-fg: #fff}.text-primary{--bslib-color-fg: #2780e3}.bg-primary{--bslib-color-bg: #2780e3;--bslib-color-fg: #fff}.text-secondary{--bslib-color-fg: #343a40}.bg-secondary{--bslib-color-bg: #343a40;--bslib-color-fg: #fff}.text-success{--bslib-color-fg: #3fb618}.bg-success{--bslib-color-bg: #3fb618;--bslib-color-fg: #fff}.text-info{--bslib-color-fg: #9954bb}.bg-info{--bslib-color-bg: #9954bb;--bslib-color-fg: #fff}.text-warning{--bslib-color-fg: #ff7518}.bg-warning{--bslib-color-bg: #ff7518;--bslib-color-fg: #fff}.text-danger{--bslib-color-fg: #ff0039}.bg-danger{--bslib-color-bg: #ff0039;--bslib-color-fg: #fff}.text-light{--bslib-color-fg: #f8f9fa}.bg-light{--bslib-color-bg: #f8f9fa;--bslib-color-fg: #000}.text-dark{--bslib-color-fg: #343a40}.bg-dark{--bslib-color-bg: #343a40;--bslib-color-fg: #fff}.bg-gradient-blue-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #4053e9;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #4053e9;color:#fff}.bg-gradient-blue-purple{--bslib-color-fg: #fff;--bslib-color-bg: #3e65ba;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #3e65ba;color:#fff}.bg-gradient-blue-pink{--bslib-color-fg: #fff;--bslib-color-bg: #7466c0;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #7466c0;color:#fff}.bg-gradient-blue-red{--bslib-color-fg: #fff;--bslib-color-bg: #7d4d9f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #7d4d9f;color:#fff}.bg-gradient-blue-orange{--bslib-color-fg: #fff;--bslib-color-bg: #7792a7;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #7792a7;color:#fff}.bg-gradient-blue-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #7d7c92;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #7d7c92;color:#fff}.bg-gradient-blue-green{--bslib-color-fg: #fff;--bslib-color-bg: #319692;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #319692;color:#fff}.bg-gradient-blue-teal{--bslib-color-fg: #fff;--bslib-color-bg: #249dc5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #249dc5;color:#fff}.bg-gradient-blue-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #556ed3;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #556ed3;color:#fff}.bg-gradient-indigo-blue{--bslib-color-fg: #fff;--bslib-color-bg: #4d3dec;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #4d3dec;color:#fff}.bg-gradient-indigo-purple{--bslib-color-fg: #fff;--bslib-color-bg: #6422c3;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #6422c3;color:#fff}.bg-gradient-indigo-pink{--bslib-color-fg: #fff;--bslib-color-bg: #9a22c9;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #9a22c9;color:#fff}.bg-gradient-indigo-red{--bslib-color-fg: #fff;--bslib-color-bg: #a30aa8;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #a30aa8;color:#fff}.bg-gradient-indigo-orange{--bslib-color-fg: #fff;--bslib-color-bg: #9d4fb0;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #9d4fb0;color:#fff}.bg-gradient-indigo-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #a3389b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #a3389b;color:#fff}.bg-gradient-indigo-green{--bslib-color-fg: #fff;--bslib-color-bg: #56529b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #56529b;color:#fff}.bg-gradient-indigo-teal{--bslib-color-fg: #fff;--bslib-color-bg: #4a5ace;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #4a5ace;color:#fff}.bg-gradient-indigo-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #7a2bdc;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #7a2bdc;color:#fff}.bg-gradient-purple-blue{--bslib-color-fg: #fff;--bslib-color-bg: #4a58a5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #4a58a5;color:#fff}.bg-gradient-purple-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #632bab;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #632bab;color:#fff}.bg-gradient-purple-pink{--bslib-color-fg: #fff;--bslib-color-bg: #973d82;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #973d82;color:#fff}.bg-gradient-purple-red{--bslib-color-fg: #fff;--bslib-color-bg: #a02561;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #a02561;color:#fff}.bg-gradient-purple-orange{--bslib-color-fg: #fff;--bslib-color-bg: #9a6a6a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #9a6a6a;color:#fff}.bg-gradient-purple-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #a05354;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #a05354;color:#fff}.bg-gradient-purple-green{--bslib-color-fg: #fff;--bslib-color-bg: #536d54;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #536d54;color:#fff}.bg-gradient-purple-teal{--bslib-color-fg: #fff;--bslib-color-bg: #477587;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #477587;color:#fff}.bg-gradient-purple-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #774695;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #774695;color:#fff}.bg-gradient-pink-blue{--bslib-color-fg: #fff;--bslib-color-bg: #9b58af;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #9b58af;color:#fff}.bg-gradient-pink-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #b42cb5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #b42cb5;color:#fff}.bg-gradient-pink-purple{--bslib-color-fg: #fff;--bslib-color-bg: #b23e86;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #b23e86;color:#fff}.bg-gradient-pink-red{--bslib-color-fg: #fff;--bslib-color-bg: #f1256b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #f1256b;color:#fff}.bg-gradient-pink-orange{--bslib-color-fg: #fff;--bslib-color-bg: #eb6a73;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #eb6a73;color:#fff}.bg-gradient-pink-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #f1545e;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #f1545e;color:#fff}.bg-gradient-pink-green{--bslib-color-fg: #fff;--bslib-color-bg: #a46e5e;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #a46e5e;color:#fff}.bg-gradient-pink-teal{--bslib-color-fg: #fff;--bslib-color-bg: #987690;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #987690;color:#fff}.bg-gradient-pink-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #c8479f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #c8479f;color:#fff}.bg-gradient-red-blue{--bslib-color-fg: #fff;--bslib-color-bg: #a9337d;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #a9337d;color:#fff}.bg-gradient-red-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #c20683;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #c20683;color:#fff}.bg-gradient-red-purple{--bslib-color-fg: #fff;--bslib-color-bg: #c01854;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #c01854;color:#fff}.bg-gradient-red-pink{--bslib-color-fg: #fff;--bslib-color-bg: #f6195a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #f6195a;color:#fff}.bg-gradient-red-orange{--bslib-color-fg: #fff;--bslib-color-bg: #f94541;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #f94541;color:#fff}.bg-gradient-red-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #ff2f2c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #ff2f2c;color:#fff}.bg-gradient-red-green{--bslib-color-fg: #fff;--bslib-color-bg: #b2492c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #b2492c;color:#fff}.bg-gradient-red-teal{--bslib-color-fg: #fff;--bslib-color-bg: #a6505f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #a6505f;color:#fff}.bg-gradient-red-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #d6226d;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #d6226d;color:#fff}.bg-gradient-orange-blue{--bslib-color-fg: #fff;--bslib-color-bg: #a09b8a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #a09b8a;color:#fff}.bg-gradient-orange-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #b96e90;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #b96e90;color:#fff}.bg-gradient-orange-purple{--bslib-color-fg: #fff;--bslib-color-bg: #b78060;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #b78060;color:#fff}.bg-gradient-orange-pink{--bslib-color-fg: #fff;--bslib-color-bg: #ed8167;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #ed8167;color:#fff}.bg-gradient-orange-red{--bslib-color-fg: #fff;--bslib-color-bg: #f66846;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #f66846;color:#fff}.bg-gradient-orange-yellow{--bslib-color-fg: #000;--bslib-color-bg: #f69738;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #f69738;color:#000}.bg-gradient-orange-green{--bslib-color-fg: #000;--bslib-color-bg: #a9b138;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #a9b138;color:#000}.bg-gradient-orange-teal{--bslib-color-fg: #000;--bslib-color-bg: #9db86b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #9db86b;color:#000}.bg-gradient-orange-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #cd897a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #cd897a;color:#fff}.bg-gradient-yellow-blue{--bslib-color-fg: #fff;--bslib-color-bg: #a97969;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #a97969;color:#fff}.bg-gradient-yellow-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #c24d6f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #c24d6f;color:#fff}.bg-gradient-yellow-purple{--bslib-color-fg: #fff;--bslib-color-bg: #c05f40;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #c05f40;color:#fff}.bg-gradient-yellow-pink{--bslib-color-fg: #fff;--bslib-color-bg: #f65f46;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #f65f46;color:#fff}.bg-gradient-yellow-red{--bslib-color-fg: #fff;--bslib-color-bg: #ff4625;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #ff4625;color:#fff}.bg-gradient-yellow-orange{--bslib-color-fg: #000;--bslib-color-bg: #f98b2e;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #f98b2e;color:#000}.bg-gradient-yellow-green{--bslib-color-fg: #fff;--bslib-color-bg: #b28f18;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #b28f18;color:#fff}.bg-gradient-yellow-teal{--bslib-color-fg: #fff;--bslib-color-bg: #a6974b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #a6974b;color:#fff}.bg-gradient-yellow-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #d66859;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #d66859;color:#fff}.bg-gradient-green-blue{--bslib-color-fg: #fff;--bslib-color-bg: #35a069;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #35a069;color:#fff}.bg-gradient-green-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #4f746f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #4f746f;color:#fff}.bg-gradient-green-purple{--bslib-color-fg: #fff;--bslib-color-bg: #4d8640;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #4d8640;color:#fff}.bg-gradient-green-pink{--bslib-color-fg: #fff;--bslib-color-bg: #838646;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #838646;color:#fff}.bg-gradient-green-red{--bslib-color-fg: #fff;--bslib-color-bg: #8c6d25;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #8c6d25;color:#fff}.bg-gradient-green-orange{--bslib-color-fg: #000;--bslib-color-bg: #86b22e;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #86b22e;color:#000}.bg-gradient-green-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #8c9c18;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #8c9c18;color:#fff}.bg-gradient-green-teal{--bslib-color-fg: #000;--bslib-color-bg: #33be4b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #33be4b;color:#000}.bg-gradient-green-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #638f59;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #638f59;color:#fff}.bg-gradient-teal-blue{--bslib-color-fg: #fff;--bslib-color-bg: #23acb5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #23acb5;color:#fff}.bg-gradient-teal-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #3c7fbb;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #3c7fbb;color:#fff}.bg-gradient-teal-purple{--bslib-color-fg: #fff;--bslib-color-bg: #3a918c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #3a918c;color:#fff}.bg-gradient-teal-pink{--bslib-color-fg: #fff;--bslib-color-bg: #709193;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #709193;color:#fff}.bg-gradient-teal-red{--bslib-color-fg: #fff;--bslib-color-bg: #797971;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #797971;color:#fff}.bg-gradient-teal-orange{--bslib-color-fg: #000;--bslib-color-bg: #73be7a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #73be7a;color:#000}.bg-gradient-teal-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #79a764;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #79a764;color:#fff}.bg-gradient-teal-green{--bslib-color-fg: #000;--bslib-color-bg: #2cc164;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #2cc164;color:#000}.bg-gradient-teal-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #509aa5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #509aa5;color:#fff}.bg-gradient-cyan-blue{--bslib-color-fg: #fff;--bslib-color-bg: #6b66cb;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #6b66cb;color:#fff}.bg-gradient-cyan-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #8539d1;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #8539d1;color:#fff}.bg-gradient-cyan-purple{--bslib-color-fg: #fff;--bslib-color-bg: #834ba2;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #834ba2;color:#fff}.bg-gradient-cyan-pink{--bslib-color-fg: #fff;--bslib-color-bg: #b94ba8;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #b94ba8;color:#fff}.bg-gradient-cyan-red{--bslib-color-fg: #fff;--bslib-color-bg: #c23287;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #c23287;color:#fff}.bg-gradient-cyan-orange{--bslib-color-fg: #fff;--bslib-color-bg: #bc788f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #bc788f;color:#fff}.bg-gradient-cyan-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #c2617a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #c2617a;color:#fff}.bg-gradient-cyan-green{--bslib-color-fg: #fff;--bslib-color-bg: #757b7a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #757b7a;color:#fff}.bg-gradient-cyan-teal{--bslib-color-fg: #fff;--bslib-color-bg: #6983ad;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #6983ad;color:#fff}.bg-blue{--bslib-color-bg: #2780e3;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-blue{--bslib-color-fg: #2780e3;color:var(--bslib-color-fg)}.bg-indigo{--bslib-color-bg: #6610f2;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-indigo{--bslib-color-fg: #6610f2;color:var(--bslib-color-fg)}.bg-purple{--bslib-color-bg: #613d7c;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-purple{--bslib-color-fg: #613d7c;color:var(--bslib-color-fg)}.bg-pink{--bslib-color-bg: #e83e8c;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-pink{--bslib-color-fg: #e83e8c;color:var(--bslib-color-fg)}.bg-red{--bslib-color-bg: #ff0039;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-red{--bslib-color-fg: #ff0039;color:var(--bslib-color-fg)}.bg-orange{--bslib-color-bg: #f0ad4e;--bslib-color-fg: #000;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-orange{--bslib-color-fg: #f0ad4e;color:var(--bslib-color-fg)}.bg-yellow{--bslib-color-bg: #ff7518;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-yellow{--bslib-color-fg: #ff7518;color:var(--bslib-color-fg)}.bg-green{--bslib-color-bg: #3fb618;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-green{--bslib-color-fg: #3fb618;color:var(--bslib-color-fg)}.bg-teal{--bslib-color-bg: #20c997;--bslib-color-fg: #000;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-teal{--bslib-color-fg: #20c997;color:var(--bslib-color-fg)}.bg-cyan{--bslib-color-bg: #9954bb;--bslib-color-fg: #fff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-cyan{--bslib-color-fg: #9954bb;color:var(--bslib-color-fg)}.text-default{--bslib-color-fg: #343a40}.bg-default{--bslib-color-bg: #343a40;--bslib-color-fg: #fff}.text-primary{--bslib-color-fg: #2780e3}.bg-primary{--bslib-color-bg: #2780e3;--bslib-color-fg: #fff}.text-secondary{--bslib-color-fg: #343a40}.bg-secondary{--bslib-color-bg: #343a40;--bslib-color-fg: #fff}.text-success{--bslib-color-fg: #3fb618}.bg-success{--bslib-color-bg: #3fb618;--bslib-color-fg: #fff}.text-info{--bslib-color-fg: #9954bb}.bg-info{--bslib-color-bg: #9954bb;--bslib-color-fg: #fff}.text-warning{--bslib-color-fg: #ff7518}.bg-warning{--bslib-color-bg: #ff7518;--bslib-color-fg: #fff}.text-danger{--bslib-color-fg: #ff0039}.bg-danger{--bslib-color-bg: #ff0039;--bslib-color-fg: #fff}.text-light{--bslib-color-fg: #f8f9fa}.bg-light{--bslib-color-bg: #f8f9fa;--bslib-color-fg: #000}.text-dark{--bslib-color-fg: #343a40}.bg-dark{--bslib-color-bg: #343a40;--bslib-color-fg: #fff}.bg-gradient-blue-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #4053e9;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #4053e9;color:#fff}.bg-gradient-blue-purple{--bslib-color-fg: #fff;--bslib-color-bg: #3e65ba;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #3e65ba;color:#fff}.bg-gradient-blue-pink{--bslib-color-fg: #fff;--bslib-color-bg: #7466c0;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #7466c0;color:#fff}.bg-gradient-blue-red{--bslib-color-fg: #fff;--bslib-color-bg: #7d4d9f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #7d4d9f;color:#fff}.bg-gradient-blue-orange{--bslib-color-fg: #fff;--bslib-color-bg: #7792a7;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #7792a7;color:#fff}.bg-gradient-blue-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #7d7c92;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #7d7c92;color:#fff}.bg-gradient-blue-green{--bslib-color-fg: #fff;--bslib-color-bg: #319692;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #319692;color:#fff}.bg-gradient-blue-teal{--bslib-color-fg: #fff;--bslib-color-bg: #249dc5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #249dc5;color:#fff}.bg-gradient-blue-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #556ed3;background:linear-gradient(var(--bg-gradient-deg, 140deg), #2780e3 var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #556ed3;color:#fff}.bg-gradient-indigo-blue{--bslib-color-fg: #fff;--bslib-color-bg: #4d3dec;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #4d3dec;color:#fff}.bg-gradient-indigo-purple{--bslib-color-fg: #fff;--bslib-color-bg: #6422c3;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #6422c3;color:#fff}.bg-gradient-indigo-pink{--bslib-color-fg: #fff;--bslib-color-bg: #9a22c9;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #9a22c9;color:#fff}.bg-gradient-indigo-red{--bslib-color-fg: #fff;--bslib-color-bg: #a30aa8;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #a30aa8;color:#fff}.bg-gradient-indigo-orange{--bslib-color-fg: #fff;--bslib-color-bg: #9d4fb0;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #9d4fb0;color:#fff}.bg-gradient-indigo-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #a3389b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #a3389b;color:#fff}.bg-gradient-indigo-green{--bslib-color-fg: #fff;--bslib-color-bg: #56529b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #56529b;color:#fff}.bg-gradient-indigo-teal{--bslib-color-fg: #fff;--bslib-color-bg: #4a5ace;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #4a5ace;color:#fff}.bg-gradient-indigo-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #7a2bdc;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #7a2bdc;color:#fff}.bg-gradient-purple-blue{--bslib-color-fg: #fff;--bslib-color-bg: #4a58a5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #4a58a5;color:#fff}.bg-gradient-purple-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #632bab;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #632bab;color:#fff}.bg-gradient-purple-pink{--bslib-color-fg: #fff;--bslib-color-bg: #973d82;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #973d82;color:#fff}.bg-gradient-purple-red{--bslib-color-fg: #fff;--bslib-color-bg: #a02561;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #a02561;color:#fff}.bg-gradient-purple-orange{--bslib-color-fg: #fff;--bslib-color-bg: #9a6a6a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #9a6a6a;color:#fff}.bg-gradient-purple-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #a05354;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #a05354;color:#fff}.bg-gradient-purple-green{--bslib-color-fg: #fff;--bslib-color-bg: #536d54;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #536d54;color:#fff}.bg-gradient-purple-teal{--bslib-color-fg: #fff;--bslib-color-bg: #477587;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #477587;color:#fff}.bg-gradient-purple-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #774695;background:linear-gradient(var(--bg-gradient-deg, 140deg), #613d7c var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #774695;color:#fff}.bg-gradient-pink-blue{--bslib-color-fg: #fff;--bslib-color-bg: #9b58af;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #9b58af;color:#fff}.bg-gradient-pink-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #b42cb5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #b42cb5;color:#fff}.bg-gradient-pink-purple{--bslib-color-fg: #fff;--bslib-color-bg: #b23e86;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #b23e86;color:#fff}.bg-gradient-pink-red{--bslib-color-fg: #fff;--bslib-color-bg: #f1256b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #f1256b;color:#fff}.bg-gradient-pink-orange{--bslib-color-fg: #fff;--bslib-color-bg: #eb6a73;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #eb6a73;color:#fff}.bg-gradient-pink-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #f1545e;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #f1545e;color:#fff}.bg-gradient-pink-green{--bslib-color-fg: #fff;--bslib-color-bg: #a46e5e;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #a46e5e;color:#fff}.bg-gradient-pink-teal{--bslib-color-fg: #fff;--bslib-color-bg: #987690;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #987690;color:#fff}.bg-gradient-pink-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #c8479f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #e83e8c var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #c8479f;color:#fff}.bg-gradient-red-blue{--bslib-color-fg: #fff;--bslib-color-bg: #a9337d;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #a9337d;color:#fff}.bg-gradient-red-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #c20683;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #c20683;color:#fff}.bg-gradient-red-purple{--bslib-color-fg: #fff;--bslib-color-bg: #c01854;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #c01854;color:#fff}.bg-gradient-red-pink{--bslib-color-fg: #fff;--bslib-color-bg: #f6195a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #f6195a;color:#fff}.bg-gradient-red-orange{--bslib-color-fg: #fff;--bslib-color-bg: #f94541;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #f94541;color:#fff}.bg-gradient-red-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #ff2f2c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #ff2f2c;color:#fff}.bg-gradient-red-green{--bslib-color-fg: #fff;--bslib-color-bg: #b2492c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #b2492c;color:#fff}.bg-gradient-red-teal{--bslib-color-fg: #fff;--bslib-color-bg: #a6505f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #a6505f;color:#fff}.bg-gradient-red-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #d6226d;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff0039 var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #d6226d;color:#fff}.bg-gradient-orange-blue{--bslib-color-fg: #fff;--bslib-color-bg: #a09b8a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #a09b8a;color:#fff}.bg-gradient-orange-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #b96e90;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #b96e90;color:#fff}.bg-gradient-orange-purple{--bslib-color-fg: #fff;--bslib-color-bg: #b78060;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #b78060;color:#fff}.bg-gradient-orange-pink{--bslib-color-fg: #fff;--bslib-color-bg: #ed8167;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #ed8167;color:#fff}.bg-gradient-orange-red{--bslib-color-fg: #fff;--bslib-color-bg: #f66846;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #f66846;color:#fff}.bg-gradient-orange-yellow{--bslib-color-fg: #000;--bslib-color-bg: #f69738;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #f69738;color:#000}.bg-gradient-orange-green{--bslib-color-fg: #000;--bslib-color-bg: #a9b138;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #a9b138;color:#000}.bg-gradient-orange-teal{--bslib-color-fg: #000;--bslib-color-bg: #9db86b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #9db86b;color:#000}.bg-gradient-orange-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #cd897a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #f0ad4e var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #cd897a;color:#fff}.bg-gradient-yellow-blue{--bslib-color-fg: #fff;--bslib-color-bg: #a97969;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #a97969;color:#fff}.bg-gradient-yellow-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #c24d6f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #c24d6f;color:#fff}.bg-gradient-yellow-purple{--bslib-color-fg: #fff;--bslib-color-bg: #c05f40;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #c05f40;color:#fff}.bg-gradient-yellow-pink{--bslib-color-fg: #fff;--bslib-color-bg: #f65f46;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #f65f46;color:#fff}.bg-gradient-yellow-red{--bslib-color-fg: #fff;--bslib-color-bg: #ff4625;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #ff4625;color:#fff}.bg-gradient-yellow-orange{--bslib-color-fg: #000;--bslib-color-bg: #f98b2e;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #f98b2e;color:#000}.bg-gradient-yellow-green{--bslib-color-fg: #fff;--bslib-color-bg: #b28f18;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #b28f18;color:#fff}.bg-gradient-yellow-teal{--bslib-color-fg: #fff;--bslib-color-bg: #a6974b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #a6974b;color:#fff}.bg-gradient-yellow-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #d66859;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ff7518 var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #d66859;color:#fff}.bg-gradient-green-blue{--bslib-color-fg: #fff;--bslib-color-bg: #35a069;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #35a069;color:#fff}.bg-gradient-green-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #4f746f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #4f746f;color:#fff}.bg-gradient-green-purple{--bslib-color-fg: #fff;--bslib-color-bg: #4d8640;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #4d8640;color:#fff}.bg-gradient-green-pink{--bslib-color-fg: #fff;--bslib-color-bg: #838646;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #838646;color:#fff}.bg-gradient-green-red{--bslib-color-fg: #fff;--bslib-color-bg: #8c6d25;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #8c6d25;color:#fff}.bg-gradient-green-orange{--bslib-color-fg: #000;--bslib-color-bg: #86b22e;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #86b22e;color:#000}.bg-gradient-green-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #8c9c18;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #8c9c18;color:#fff}.bg-gradient-green-teal{--bslib-color-fg: #000;--bslib-color-bg: #33be4b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #33be4b;color:#000}.bg-gradient-green-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #638f59;background:linear-gradient(var(--bg-gradient-deg, 140deg), #3fb618 var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #638f59;color:#fff}.bg-gradient-teal-blue{--bslib-color-fg: #fff;--bslib-color-bg: #23acb5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #23acb5;color:#fff}.bg-gradient-teal-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #3c7fbb;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #3c7fbb;color:#fff}.bg-gradient-teal-purple{--bslib-color-fg: #fff;--bslib-color-bg: #3a918c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #3a918c;color:#fff}.bg-gradient-teal-pink{--bslib-color-fg: #fff;--bslib-color-bg: #709193;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #709193;color:#fff}.bg-gradient-teal-red{--bslib-color-fg: #fff;--bslib-color-bg: #797971;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #797971;color:#fff}.bg-gradient-teal-orange{--bslib-color-fg: #000;--bslib-color-bg: #73be7a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #73be7a;color:#000}.bg-gradient-teal-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #79a764;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #79a764;color:#fff}.bg-gradient-teal-green{--bslib-color-fg: #000;--bslib-color-bg: #2cc164;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #2cc164;color:#000}.bg-gradient-teal-cyan{--bslib-color-fg: #fff;--bslib-color-bg: #509aa5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #9954bb var(--bg-gradient-end, 180%)) #509aa5;color:#fff}.bg-gradient-cyan-blue{--bslib-color-fg: #fff;--bslib-color-bg: #6b66cb;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #2780e3 var(--bg-gradient-end, 180%)) #6b66cb;color:#fff}.bg-gradient-cyan-indigo{--bslib-color-fg: #fff;--bslib-color-bg: #8539d1;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #8539d1;color:#fff}.bg-gradient-cyan-purple{--bslib-color-fg: #fff;--bslib-color-bg: #834ba2;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #613d7c var(--bg-gradient-end, 180%)) #834ba2;color:#fff}.bg-gradient-cyan-pink{--bslib-color-fg: #fff;--bslib-color-bg: #b94ba8;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #e83e8c var(--bg-gradient-end, 180%)) #b94ba8;color:#fff}.bg-gradient-cyan-red{--bslib-color-fg: #fff;--bslib-color-bg: #c23287;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #ff0039 var(--bg-gradient-end, 180%)) #c23287;color:#fff}.bg-gradient-cyan-orange{--bslib-color-fg: #fff;--bslib-color-bg: #bc788f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #f0ad4e var(--bg-gradient-end, 180%)) #bc788f;color:#fff}.bg-gradient-cyan-yellow{--bslib-color-fg: #fff;--bslib-color-bg: #c2617a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #ff7518 var(--bg-gradient-end, 180%)) #c2617a;color:#fff}.bg-gradient-cyan-green{--bslib-color-fg: #fff;--bslib-color-bg: #757b7a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #3fb618 var(--bg-gradient-end, 180%)) #757b7a;color:#fff}.bg-gradient-cyan-teal{--bslib-color-fg: #fff;--bslib-color-bg: #6983ad;background:linear-gradient(var(--bg-gradient-deg, 140deg), #9954bb var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #6983ad;color:#fff}:root{--bslib-spacer: 1rem;--bslib-mb-spacer: var(--bslib-spacer, 1rem)}.bslib-mb-spacing{margin-bottom:var(--bslib-mb-spacer)}.bslib-gap-spacing{gap:var(--bslib-mb-spacer)}.bslib-gap-spacing>.bslib-mb-spacing,.bslib-gap-spacing>.form-group,.bslib-gap-spacing>p,.bslib-gap-spacing>pre{margin-bottom:0}.html-fill-container>.html-fill-item.bslib-mb-spacing{margin-bottom:0}.tab-content>.tab-pane.html-fill-container{display:none}.tab-content>.active.html-fill-container{display:flex}.tab-content.html-fill-container{padding:0}.accordion .accordion-header{font-size:calc(1.29rem + 0.48vw);margin-top:0;margin-bottom:.5rem;font-weight:400;line-height:1.2;color:var(--bs-heading-color);margin-bottom:0}@media(min-width: 1200px){.accordion .accordion-header{font-size:1.65rem}}.accordion .accordion-icon:not(:empty){margin-right:.75rem;display:flex}.accordion .accordion-button:not(.collapsed){box-shadow:none}.accordion .accordion-button:not(.collapsed):focus{box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.bslib-card{overflow:auto}.bslib-card .card-body+.card-body{padding-top:0}.bslib-card .card-body{overflow:auto}.bslib-card .card-body p{margin-top:0}.bslib-card .card-body p:last-child{margin-bottom:0}.bslib-card .card-body{max-height:var(--bslib-card-body-max-height, none)}.bslib-card[data-full-screen=true]>.card-body{max-height:var(--bslib-card-body-max-height-full-screen, none)}.bslib-card .card-header .form-group{margin-bottom:0}.bslib-card .card-header .selectize-control{margin-bottom:0}.bslib-card .card-header .selectize-control .item{margin-right:1.15rem}.bslib-card .card-footer{margin-top:auto}.bslib-card .bslib-navs-card-title{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center}.bslib-card .bslib-navs-card-title .nav{margin-left:auto}.bslib-card .bslib-sidebar-layout:not([data-bslib-sidebar-border=true]){border:none}.bslib-card .bslib-sidebar-layout:not([data-bslib-sidebar-border-radius=true]){border-top-left-radius:0;border-top-right-radius:0}[data-full-screen=true]{position:fixed;inset:3.5rem 1rem 1rem;height:auto !important;max-height:none !important;width:auto !important;z-index:1070}.bslib-full-screen-enter{display:none;position:absolute;bottom:var(--bslib-full-screen-enter-bottom, 0.2rem);right:var(--bslib-full-screen-enter-right, 0);top:var(--bslib-full-screen-enter-top);left:var(--bslib-full-screen-enter-left);color:var(--bslib-color-fg, var(--bs-card-color));background-color:var(--bslib-color-bg, var(--bs-card-bg, var(--bs-body-bg)));border:var(--bs-card-border-width) solid var(--bslib-color-fg, var(--bs-card-border-color));box-shadow:0 2px 4px rgba(0,0,0,.15);margin:.2rem .4rem;padding:.55rem !important;font-size:.8rem;cursor:pointer;opacity:.7;z-index:1070}.bslib-full-screen-enter:hover{opacity:1}.card[data-full-screen=false]:hover>*>.bslib-full-screen-enter{display:block}.bslib-has-full-screen .card:hover>*>.bslib-full-screen-enter{display:none}@media(max-width: 575.98px){.bslib-full-screen-enter{display:none !important}}.bslib-full-screen-exit{position:relative;top:1.35rem;font-size:.9rem;cursor:pointer;text-decoration:none;display:flex;float:right;margin-right:2.15rem;align-items:center;color:rgba(var(--bs-body-bg-rgb), 0.8)}.bslib-full-screen-exit:hover{color:rgba(var(--bs-body-bg-rgb), 1)}.bslib-full-screen-exit svg{margin-left:.5rem;font-size:1.5rem}#bslib-full-screen-overlay{position:fixed;inset:0;background-color:rgba(var(--bs-body-color-rgb), 0.6);backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);z-index:1069;animation:bslib-full-screen-overlay-enter 400ms cubic-bezier(0.6, 0.02, 0.65, 1) forwards}@keyframes bslib-full-screen-overlay-enter{0%{opacity:0}100%{opacity:1}}.bslib-grid{display:grid !important;gap:var(--bslib-spacer, 1rem);height:var(--bslib-grid-height)}.bslib-grid.grid{grid-template-columns:repeat(var(--bs-columns, 12), minmax(0, 1fr));grid-template-rows:unset;grid-auto-rows:var(--bslib-grid--row-heights);--bslib-grid--row-heights--xs: unset;--bslib-grid--row-heights--sm: unset;--bslib-grid--row-heights--md: unset;--bslib-grid--row-heights--lg: unset;--bslib-grid--row-heights--xl: unset;--bslib-grid--row-heights--xxl: unset}.bslib-grid.grid.bslib-grid--row-heights--xs{--bslib-grid--row-heights: var(--bslib-grid--row-heights--xs)}@media(min-width: 576px){.bslib-grid.grid.bslib-grid--row-heights--sm{--bslib-grid--row-heights: var(--bslib-grid--row-heights--sm)}}@media(min-width: 768px){.bslib-grid.grid.bslib-grid--row-heights--md{--bslib-grid--row-heights: var(--bslib-grid--row-heights--md)}}@media(min-width: 992px){.bslib-grid.grid.bslib-grid--row-heights--lg{--bslib-grid--row-heights: var(--bslib-grid--row-heights--lg)}}@media(min-width: 1200px){.bslib-grid.grid.bslib-grid--row-heights--xl{--bslib-grid--row-heights: var(--bslib-grid--row-heights--xl)}}@media(min-width: 1400px){.bslib-grid.grid.bslib-grid--row-heights--xxl{--bslib-grid--row-heights: var(--bslib-grid--row-heights--xxl)}}.bslib-grid>*>.shiny-input-container{width:100%}.bslib-grid-item{grid-column:auto/span 1}@media(max-width: 767.98px){.bslib-grid-item{grid-column:1/-1}}@media(max-width: 575.98px){.bslib-grid{grid-template-columns:1fr !important;height:var(--bslib-grid-height-mobile)}.bslib-grid.grid{height:unset !important;grid-auto-rows:var(--bslib-grid--row-heights--xs, auto)}}@media(min-width: 576px){.nav:not(.nav-hidden){display:flex !important;display:-webkit-flex !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column){float:none !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column)>.bslib-nav-spacer{margin-left:auto !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column)>.form-inline{margin-top:auto;margin-bottom:auto}.nav:not(.nav-hidden).nav-stacked{flex-direction:column;-webkit-flex-direction:column;height:100%}.nav:not(.nav-hidden).nav-stacked>.bslib-nav-spacer{margin-top:auto !important}}html{height:100%}.bslib-page-fill{width:100%;height:100%;margin:0;padding:var(--bslib-spacer, 1rem);gap:var(--bslib-spacer, 1rem)}@media(max-width: 575.98px){.bslib-page-fill{height:var(--bslib-page-fill-mobile-height, auto)}}.navbar+.container-fluid:has(>.tab-content>.tab-pane.active.html-fill-container),.navbar+.container-sm:has(>.tab-content>.tab-pane.active.html-fill-container),.navbar+.container-md:has(>.tab-content>.tab-pane.active.html-fill-container),.navbar+.container-lg:has(>.tab-content>.tab-pane.active.html-fill-container),.navbar+.container-xl:has(>.tab-content>.tab-pane.active.html-fill-container),.navbar+.container-xxl:has(>.tab-content>.tab-pane.active.html-fill-container){padding-left:0;padding-right:0}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container,.navbar+.container-sm>.tab-content>.tab-pane.active.html-fill-container,.navbar+.container-md>.tab-content>.tab-pane.active.html-fill-container,.navbar+.container-lg>.tab-content>.tab-pane.active.html-fill-container,.navbar+.container-xl>.tab-content>.tab-pane.active.html-fill-container,.navbar+.container-xxl>.tab-content>.tab-pane.active.html-fill-container{padding:var(--bslib-spacer, 1rem);gap:var(--bslib-spacer, 1rem)}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container:has(>.bslib-sidebar-layout:only-child),.navbar+.container-sm>.tab-content>.tab-pane.active.html-fill-container:has(>.bslib-sidebar-layout:only-child),.navbar+.container-md>.tab-content>.tab-pane.active.html-fill-container:has(>.bslib-sidebar-layout:only-child),.navbar+.container-lg>.tab-content>.tab-pane.active.html-fill-container:has(>.bslib-sidebar-layout:only-child),.navbar+.container-xl>.tab-content>.tab-pane.active.html-fill-container:has(>.bslib-sidebar-layout:only-child),.navbar+.container-xxl>.tab-content>.tab-pane.active.html-fill-container:has(>.bslib-sidebar-layout:only-child){padding:0}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border=true]),.navbar+.container-sm>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border=true]),.navbar+.container-md>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border=true]),.navbar+.container-lg>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border=true]),.navbar+.container-xl>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border=true]),.navbar+.container-xxl>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border=true]){border-left:none;border-right:none;border-bottom:none}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border-radius=true]),.navbar+.container-sm>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border-radius=true]),.navbar+.container-md>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border-radius=true]),.navbar+.container-lg>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border-radius=true]),.navbar+.container-xl>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border-radius=true]),.navbar+.container-xxl>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border-radius=true]){border-radius:0}.navbar+div>.bslib-sidebar-layout{border-top:var(--bslib-sidebar-border)}:root{--bslib-page-sidebar-title-bg: #2780e3;--bslib-page-sidebar-title-color: #fff}.bslib-page-title{background-color:var(--bslib-page-sidebar-title-bg);color:var(--bslib-page-sidebar-title-color);font-size:1.25rem;font-weight:300;padding:var(--bslib-spacer, 1rem);padding-left:1.5rem;margin-bottom:0;border-bottom:1px solid #dee2e6}.bslib-sidebar-layout{--bslib-sidebar-transition-duration: 500ms;--bslib-sidebar-transition-easing-x: cubic-bezier(0.8, 0.78, 0.22, 1.07);--bslib-sidebar-border: var(--bs-card-border-width, 1px) solid var(--bs-card-border-color, rgba(0, 0, 0, 0.175));--bslib-sidebar-border-radius: var(--bs-border-radius);--bslib-sidebar-vert-border: var(--bs-card-border-width, 1px) solid var(--bs-card-border-color, rgba(0, 0, 0, 0.175));--bslib-sidebar-bg: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.05);--bslib-sidebar-fg: var(--bs-emphasis-color, black);--bslib-sidebar-main-fg: var(--bs-card-color, var(--bs-body-color));--bslib-sidebar-main-bg: var(--bs-card-bg, var(--bs-body-bg));--bslib-sidebar-toggle-bg: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.1);--bslib-sidebar-padding: calc(var(--bslib-spacer) * 1.5);--bslib-sidebar-icon-size: var(--bslib-spacer, 1rem);--bslib-sidebar-icon-button-size: calc(var(--bslib-sidebar-icon-size, 1rem) * 2);--bslib-sidebar-padding-icon: calc(var(--bslib-sidebar-icon-button-size, 2rem) * 1.5);--bslib-collapse-toggle-border-radius: var(--bs-border-radius, 0.25rem);--bslib-collapse-toggle-transform: 0deg;--bslib-sidebar-toggle-transition-easing: cubic-bezier(1, 0, 0, 1);--bslib-collapse-toggle-right-transform: 180deg;--bslib-sidebar-column-main: minmax(0, 1fr);display:grid !important;grid-template-columns:min(100% - var(--bslib-sidebar-icon-size),var(--bslib-sidebar-width, 250px)) var(--bslib-sidebar-column-main);position:relative;transition:grid-template-columns ease-in-out var(--bslib-sidebar-transition-duration);border:var(--bslib-sidebar-border);border-radius:var(--bslib-sidebar-border-radius)}@media(prefers-reduced-motion: reduce){.bslib-sidebar-layout{transition:none}}.bslib-sidebar-layout[data-bslib-sidebar-border=false]{border:none}.bslib-sidebar-layout[data-bslib-sidebar-border-radius=false]{border-radius:initial}.bslib-sidebar-layout>.main,.bslib-sidebar-layout>.sidebar{grid-row:1/2;border-radius:inherit;overflow:auto}.bslib-sidebar-layout>.main{grid-column:2/3;border-top-left-radius:0;border-bottom-left-radius:0;padding:var(--bslib-sidebar-padding);transition:padding var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration);color:var(--bslib-sidebar-main-fg);background-color:var(--bslib-sidebar-main-bg)}.bslib-sidebar-layout>.sidebar{grid-column:1/2;width:100%;height:100%;border-right:var(--bslib-sidebar-vert-border);border-top-right-radius:0;border-bottom-right-radius:0;color:var(--bslib-sidebar-fg);background-color:var(--bslib-sidebar-bg);backdrop-filter:blur(5px)}.bslib-sidebar-layout>.sidebar>.sidebar-content{display:flex;flex-direction:column;gap:var(--bslib-spacer, 1rem);padding:var(--bslib-sidebar-padding);padding-top:var(--bslib-sidebar-padding-icon)}.bslib-sidebar-layout>.sidebar>.sidebar-content>:last-child:not(.sidebar-title){margin-bottom:0}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion{margin-left:calc(-1*var(--bslib-sidebar-padding));margin-right:calc(-1*var(--bslib-sidebar-padding))}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:last-child{margin-bottom:calc(-1*var(--bslib-sidebar-padding))}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:not(:last-child){margin-bottom:1rem}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion .accordion-body{display:flex;flex-direction:column}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:not(:first-child) .accordion-item:first-child{border-top:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:not(:last-child) .accordion-item:last-child{border-bottom:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.bslib-sidebar-layout>.sidebar>.sidebar-content.has-accordion>.sidebar-title{border-bottom:none;padding-bottom:0}.bslib-sidebar-layout>.sidebar .shiny-input-container{width:100%}.bslib-sidebar-layout[data-bslib-sidebar-open=always]>.sidebar>.sidebar-content{padding-top:var(--bslib-sidebar-padding)}.bslib-sidebar-layout>.collapse-toggle{grid-row:1/2;grid-column:1/2;display:inline-flex;align-items:center;position:absolute;right:calc(var(--bslib-sidebar-icon-size));top:calc(var(--bslib-sidebar-icon-size, 1rem)/2);border:none;border-radius:var(--bslib-collapse-toggle-border-radius);height:var(--bslib-sidebar-icon-button-size, 2rem);width:var(--bslib-sidebar-icon-button-size, 2rem);display:flex;align-items:center;justify-content:center;padding:0;color:var(--bslib-sidebar-fg);background-color:unset;transition:color var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration),top var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration),right var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration),left var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration)}.bslib-sidebar-layout>.collapse-toggle:hover{background-color:var(--bslib-sidebar-toggle-bg)}.bslib-sidebar-layout>.collapse-toggle>.collapse-icon{opacity:.8;width:var(--bslib-sidebar-icon-size);height:var(--bslib-sidebar-icon-size);transform:rotateY(var(--bslib-collapse-toggle-transform));transition:transform var(--bslib-sidebar-toggle-transition-easing) var(--bslib-sidebar-transition-duration)}.bslib-sidebar-layout>.collapse-toggle:hover>.collapse-icon{opacity:1}.bslib-sidebar-layout .sidebar-title{font-size:1.25rem;line-height:1.25;margin-top:0;margin-bottom:1rem;padding-bottom:1rem;border-bottom:var(--bslib-sidebar-border)}.bslib-sidebar-layout.sidebar-right{grid-template-columns:var(--bslib-sidebar-column-main) min(100% - var(--bslib-sidebar-icon-size),var(--bslib-sidebar-width, 250px))}.bslib-sidebar-layout.sidebar-right>.main{grid-column:1/2;border-top-right-radius:0;border-bottom-right-radius:0;border-top-left-radius:inherit;border-bottom-left-radius:inherit}.bslib-sidebar-layout.sidebar-right>.sidebar{grid-column:2/3;border-right:none;border-left:var(--bslib-sidebar-vert-border);border-top-left-radius:0;border-bottom-left-radius:0}.bslib-sidebar-layout.sidebar-right>.collapse-toggle{grid-column:2/3;left:var(--bslib-sidebar-icon-size);right:unset;border:var(--bslib-collapse-toggle-border)}.bslib-sidebar-layout.sidebar-right>.collapse-toggle>.collapse-icon{transform:rotateY(var(--bslib-collapse-toggle-right-transform))}.bslib-sidebar-layout.sidebar-collapsed{--bslib-collapse-toggle-transform: 180deg;--bslib-collapse-toggle-right-transform: 0deg;--bslib-sidebar-vert-border: none;grid-template-columns:0 minmax(0, 1fr)}.bslib-sidebar-layout.sidebar-collapsed.sidebar-right{grid-template-columns:minmax(0, 1fr) 0}.bslib-sidebar-layout.sidebar-collapsed:not(.transitioning)>.sidebar>*{display:none}.bslib-sidebar-layout.sidebar-collapsed>.main{border-radius:inherit}.bslib-sidebar-layout.sidebar-collapsed:not(.sidebar-right)>.main{padding-left:var(--bslib-sidebar-padding-icon)}.bslib-sidebar-layout.sidebar-collapsed.sidebar-right>.main{padding-right:var(--bslib-sidebar-padding-icon)}.bslib-sidebar-layout.sidebar-collapsed>.collapse-toggle{color:var(--bslib-sidebar-main-fg);top:calc(var(--bslib-sidebar-overlap-counter, 0)*(var(--bslib-sidebar-icon-size) + var(--bslib-sidebar-padding)) + var(--bslib-sidebar-icon-size, 1rem)/2);right:calc(-2.5*var(--bslib-sidebar-icon-size) - var(--bs-card-border-width, 1px))}.bslib-sidebar-layout.sidebar-collapsed.sidebar-right>.collapse-toggle{left:calc(-2.5*var(--bslib-sidebar-icon-size) - var(--bs-card-border-width, 1px));right:unset}@media(min-width: 576px){.bslib-sidebar-layout.transitioning>.sidebar>.sidebar-content{display:none}}@media(max-width: 575.98px){.bslib-sidebar-layout[data-bslib-sidebar-open=desktop]{--bslib-sidebar-js-init-collapsed: true}.bslib-sidebar-layout>.sidebar,.bslib-sidebar-layout.sidebar-right>.sidebar{border:none}.bslib-sidebar-layout>.main,.bslib-sidebar-layout.sidebar-right>.main{grid-column:1/3}.bslib-sidebar-layout[data-bslib-sidebar-open=always]{display:block !important}.bslib-sidebar-layout[data-bslib-sidebar-open=always]>.sidebar{max-height:var(--bslib-sidebar-max-height-mobile);overflow-y:auto;border-top:var(--bslib-sidebar-vert-border)}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]){grid-template-columns:100% 0}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]):not(.sidebar-collapsed)>.sidebar{z-index:1}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]):not(.sidebar-collapsed)>.collapse-toggle{z-index:1}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]).sidebar-right{grid-template-columns:0 100%}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]).sidebar-collapsed{grid-template-columns:0 100%}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]).sidebar-collapsed.sidebar-right{grid-template-columns:100% 0}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]):not(.sidebar-right)>.main{padding-left:var(--bslib-sidebar-padding-icon)}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]).sidebar-right>.main{padding-right:var(--bslib-sidebar-padding-icon)}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always])>.main{opacity:0;transition:opacity var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration)}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]).sidebar-collapsed>.main{opacity:1}}:root{--bslib-value-box-shadow: none;--bslib-value-box-border-width-auto-yes: var(--bslib-value-box-border-width-baseline);--bslib-value-box-border-width-auto-no: 0;--bslib-value-box-border-width-baseline: 1px}.bslib-value-box{border-width:var(--bslib-value-box-border-width-auto-no, var(--bslib-value-box-border-width-baseline));container-name:bslib-value-box;container-type:inline-size}.bslib-value-box.card{box-shadow:var(--bslib-value-box-shadow)}.bslib-value-box.border-auto{border-width:var(--bslib-value-box-border-width-auto-yes, var(--bslib-value-box-border-width-baseline))}.bslib-value-box.default{--bslib-value-box-bg-default: var(--bs-card-bg, #fff);--bslib-value-box-border-color-default: var(--bs-card-border-color, rgba(0, 0, 0, 0.175));color:var(--bslib-value-box-color);background-color:var(--bslib-value-box-bg, var(--bslib-value-box-bg-default));border-color:var(--bslib-value-box-border-color, var(--bslib-value-box-border-color-default))}.bslib-value-box .value-box-grid{display:grid;grid-template-areas:"left right";align-items:center;overflow:hidden}.bslib-value-box .value-box-showcase{height:100%;max-height:var(---bslib-value-box-showcase-max-h, 100%)}.bslib-value-box .value-box-showcase,.bslib-value-box .value-box-showcase>.html-fill-item{width:100%}.bslib-value-box[data-full-screen=true] .value-box-showcase{max-height:var(---bslib-value-box-showcase-max-h-fs, 100%)}@media screen and (min-width: 575.98px){@container bslib-value-box (max-width: 300px){.bslib-value-box:not(.showcase-bottom) .value-box-grid{grid-template-columns:1fr !important;grid-template-rows:auto auto;grid-template-areas:"top" "bottom"}.bslib-value-box:not(.showcase-bottom) .value-box-grid .value-box-showcase{grid-area:top !important}.bslib-value-box:not(.showcase-bottom) .value-box-grid .value-box-area{grid-area:bottom !important;justify-content:end}}}.bslib-value-box .value-box-area{justify-content:center;padding:1.5rem 1rem;font-size:.9rem;font-weight:500}.bslib-value-box .value-box-area *{margin-bottom:0;margin-top:0}.bslib-value-box .value-box-title{font-size:1rem;margin-top:0;margin-bottom:.5rem;font-weight:400;line-height:1.2}.bslib-value-box .value-box-title:empty::after{content:" "}.bslib-value-box .value-box-value{font-size:calc(1.29rem + 0.48vw);margin-top:0;margin-bottom:.5rem;font-weight:400;line-height:1.2}@media(min-width: 1200px){.bslib-value-box .value-box-value{font-size:1.65rem}}.bslib-value-box .value-box-value:empty::after{content:" "}.bslib-value-box .value-box-showcase{align-items:center;justify-content:center;margin-top:auto;margin-bottom:auto;padding:1rem}.bslib-value-box .value-box-showcase .bi,.bslib-value-box .value-box-showcase .fa,.bslib-value-box .value-box-showcase .fab,.bslib-value-box .value-box-showcase .fas,.bslib-value-box .value-box-showcase .far{opacity:.85;min-width:50px;max-width:125%}.bslib-value-box .value-box-showcase .bi,.bslib-value-box .value-box-showcase .fa,.bslib-value-box .value-box-showcase .fab,.bslib-value-box .value-box-showcase .fas,.bslib-value-box .value-box-showcase .far{font-size:4rem}.bslib-value-box.showcase-top-right .value-box-grid{grid-template-columns:1fr var(---bslib-value-box-showcase-w, 50%)}.bslib-value-box.showcase-top-right .value-box-grid .value-box-showcase{grid-area:right;margin-left:auto;align-self:start;align-items:end;padding-left:0;padding-bottom:0}.bslib-value-box.showcase-top-right .value-box-grid .value-box-area{grid-area:left;align-self:end}.bslib-value-box.showcase-top-right[data-full-screen=true] .value-box-grid{grid-template-columns:auto var(---bslib-value-box-showcase-w-fs, 1fr)}.bslib-value-box.showcase-top-right[data-full-screen=true] .value-box-grid>div{align-self:center}.bslib-value-box.showcase-top-right:not([data-full-screen=true]) .value-box-showcase{margin-top:0}@container bslib-value-box (max-width: 300px){.bslib-value-box.showcase-top-right:not([data-full-screen=true]) .value-box-grid .value-box-showcase{padding-left:1rem}}.bslib-value-box.showcase-left-center .value-box-grid{grid-template-columns:var(---bslib-value-box-showcase-w, 30%) auto}.bslib-value-box.showcase-left-center[data-full-screen=true] .value-box-grid{grid-template-columns:var(---bslib-value-box-showcase-w-fs, 1fr) auto}.bslib-value-box.showcase-left-center:not([data-fill-screen=true]) .value-box-grid .value-box-showcase{grid-area:left}.bslib-value-box.showcase-left-center:not([data-fill-screen=true]) .value-box-grid .value-box-area{grid-area:right}.bslib-value-box.showcase-bottom .value-box-grid{grid-template-columns:1fr;grid-template-rows:1fr var(---bslib-value-box-showcase-h, auto);grid-template-areas:"top" "bottom";overflow:hidden}.bslib-value-box.showcase-bottom .value-box-grid .value-box-showcase{grid-area:bottom;padding:0;margin:0}.bslib-value-box.showcase-bottom .value-box-grid .value-box-area{grid-area:top}.bslib-value-box.showcase-bottom[data-full-screen=true] .value-box-grid{grid-template-rows:1fr var(---bslib-value-box-showcase-h-fs, 2fr)}.bslib-value-box.showcase-bottom[data-full-screen=true] .value-box-grid .value-box-showcase{padding:1rem}[data-bs-theme=dark] .bslib-value-box{--bslib-value-box-shadow: 0 0.5rem 1rem rgb(0 0 0 / 50%)}.html-fill-container{display:flex;flex-direction:column;min-height:0;min-width:0}.html-fill-container>.html-fill-item{flex:1 1 auto;min-height:0;min-width:0}.html-fill-container>:not(.html-fill-item){flex:0 0 auto}.quarto-container{min-height:calc(100vh - 132px)}body.hypothesis-enabled #quarto-header{margin-right:16px}footer.footer .nav-footer,#quarto-header>nav{padding-left:1em;padding-right:1em}footer.footer div.nav-footer p:first-child{margin-top:0}footer.footer div.nav-footer p:last-child{margin-bottom:0}#quarto-content>*{padding-top:14px}#quarto-content>#quarto-sidebar-glass{padding-top:0px}@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:position 200ms linear;transition:all 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% - 115px);min-width:0;display:flex;align-items:center}@media(min-width: 992px){.navbar-brand-container{margin-right:1em}}.navbar-brand.navbar-brand-logo{margin-right:4px;display:inline-flex}.navbar-toggler{flex-basis:content;flex-shrink:0}.navbar .navbar-brand-container{order:2}.navbar .navbar-toggler{order:1}.navbar .navbar-container>.navbar-nav{order:20}.navbar .navbar-container>.navbar-brand-container{margin-left:0 !important;margin-right:0 !important}.navbar .navbar-collapse{order:20}.navbar #quarto-search{order:4;margin-left:auto}.navbar .navbar-toggler{margin-right:.5em}.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 .quarto-navbar-tools{order:3}.navbar .quarto-navbar-tools div.dropdown{display:inline-block}.navbar .quarto-navbar-tools .quarto-navigation-tool{color:#fdfeff}.navbar .quarto-navbar-tools .quarto-navigation-tool:hover{color:#fdfdff}.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}@media(max-width: 991.98px){#quarto-sidebar div.sidebar-header{padding-top:.2em}}.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 .quarto-navigation-tool{opacity:.7;font-size:.875rem}#quarto-sidebar>nav>.sidebar-tools-main{margin-left:14px}.sidebar-tools-main{display:inline-flex;margin-left:0px;order:2}.sidebar-tools-main:not(.tools-wide){vertical-align:middle}.sidebar-navigation .quarto-navigation-tool.dropdown-toggle::after{display:none}.sidebar.sidebar-navigation>*{padding-top:1em}.sidebar-item{margin-bottom:.2em;line-height:1rem;margin-top:.4rem}.sidebar-section{padding-left:.5em;padding-bottom:.2em}.sidebar-item .sidebar-item-container{display:flex;justify-content:space-between;cursor:pointer}.sidebar-item-toggle:hover{cursor:pointer}.sidebar-item .sidebar-item-toggle .bi{font-size:.7rem;text-align:center}.sidebar-item .sidebar-item-toggle .bi-chevron-right::before{transition:transform 200ms ease}.sidebar-item .sidebar-item-toggle[aria-expanded=false] .bi-chevron-right::before{transform:none}.sidebar-item .sidebar-item-toggle[aria-expanded=true] .bi-chevron-right::before{transform:rotate(90deg)}.sidebar-item-text{width:100%}.sidebar-navigation .sidebar-divider{margin-left:0;margin-right:0;margin-top:.5rem;margin-bottom:.5rem}@media(max-width: 991.98px){.quarto-secondary-nav{display:block}.quarto-secondary-nav button.quarto-search-button{padding-right:0em;padding-left:2em}.quarto-secondary-nav button.quarto-btn-toggle{margin-left:-0.75rem;margin-right:.15rem}.quarto-secondary-nav nav.quarto-title-breadcrumbs{display:none}.quarto-secondary-nav nav.quarto-page-breadcrumbs{display:flex;align-items:center;padding-right:1em;margin-left:-0.25em}.quarto-secondary-nav nav.quarto-page-breadcrumbs a{text-decoration:none}.quarto-secondary-nav nav.quarto-page-breadcrumbs ol.breadcrumb{margin-bottom:0}}@media(min-width: 992px){.quarto-secondary-nav{display:none}}.quarto-title-breadcrumbs .breadcrumb{margin-bottom:.5em;font-size:.9rem}.quarto-title-breadcrumbs .breadcrumb li:last-of-type a{color:#6c757d}.quarto-secondary-nav .quarto-btn-toggle{color:#595959}.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}.no-decor{text-decoration:none}.quarto-secondary-nav-title{margin-top:.3em;color:#595959;padding-top:4px}.quarto-secondary-nav nav.quarto-page-breadcrumbs{color:#595959}.quarto-secondary-nav nav.quarto-page-breadcrumbs a{color:#595959}.quarto-secondary-nav nav.quarto-page-breadcrumbs a:hover{color:rgba(33,81,191,.8)}.quarto-secondary-nav nav.quarto-page-breadcrumbs .breadcrumb-item::before{color:#8c8c8c}.breadcrumb-item{line-height:1.2rem}div.sidebar-item-container{color:#595959}div.sidebar-item-container:hover,div.sidebar-item-container:focus{color:rgba(33,81,191,.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:#2151bf}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{min-height:100%}nav.quarto-secondary-nav{background-color:#fff;border-bottom:1px solid #dee2e6}.quarto-banner nav.quarto-secondary-nav{background-color:#2780e3;color:#fdfeff;border-top:1px solid #dee2e6}.sidebar .sidebar-footer{visibility:visible;padding-top:1rem;position:inherit}.sidebar-tools-collapse{display:block}}#quarto-sidebar{transition:width .15s ease-in}#quarto-sidebar>*{padding-right:1em}@media(max-width: 991.98px){#quarto-sidebar .sidebar-menu-container{white-space:nowrap;min-width:225px}#quarto-sidebar.show{transition:width .15s ease-out}}@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}}@media(max-width: 991.98px){#quarto-sidebar-glass{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(255,255,255,0);transition:background-color .15s ease-in;z-index:-1}#quarto-sidebar-glass.collapsing{z-index:1000}#quarto-sidebar-glass.show{transition:background-color .15s ease-out;background-color:rgba(102,102,102,.4);z-index:1000}}.sidebar .sidebar-footer{padding:.5rem 1rem;align-self:flex-end;color:#6c757d;width:100%}.quarto-page-breadcrumbs .breadcrumb-item+.breadcrumb-item,.quarto-page-breadcrumbs .breadcrumb-item{padding-right:.33em;padding-left:0}.quarto-page-breadcrumbs .breadcrumb-item::before{padding-right:.33em}.quarto-sidebar-footer{font-size:.875em}.sidebar-section .bi-chevron-right{vertical-align:middle}.sidebar-section .bi-chevron-right::before{font-size:.9em}.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:#1f4eb6}.nav-footer .toc-actions{padding-bottom:.5em;padding-top:.5em}.nav-footer .toc-actions a,.nav-footer .toc-actions a:hover{text-decoration:none}.nav-footer .toc-actions ul{display:flex;list-style:none}.nav-footer .toc-actions ul :first-child{margin-left:auto}.nav-footer .toc-actions ul :last-child{margin-right:auto}.nav-footer .toc-actions ul li{padding-right:1.5em}.nav-footer .toc-actions ul li i.bi{padding-right:.4em}.nav-footer .toc-actions ul li:last-of-type{padding-right:0}.nav-footer{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:baseline;text-align:center;padding-top:.5rem;padding-bottom:.5rem;background-color:#fff}body.nav-fixed{padding-top:64px}.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:inline-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{flex:1 1 0px;text-align:left}.nav-footer-right{flex:1 1 0px;text-align:right}.nav-footer-center{flex:1 1 0px;min-height:3em;text-align:center}.nav-footer-center .footer-items{justify-content:center}@media(max-width: 767.98px){.nav-footer-center{margin-top:3em}}.navbar .quarto-reader-toggle.reader .quarto-reader-toggle-btn{background-color:#fdfeff;border-radius:3px}@media(max-width: 991.98px){.quarto-reader-toggle{display:none}}.quarto-reader-toggle.reader.quarto-navigation-tool .quarto-reader-toggle-btn{background-color:#595959;border-radius:3px}.quarto-reader-toggle .quarto-reader-toggle-btn{display:inline-flex;padding-left:.2em;padding-right:.2em;margin-left:-0.2em;margin-right:-0.2em;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,')}#quarto-back-to-top{display:none;position:fixed;bottom:50px;background-color:#fff;border-radius:.25rem;box-shadow:0 .2rem .5rem #6c757d,0 0 .05rem #6c757d;color:#6c757d;text-decoration:none;font-size:.9em;text-align:center;left:50%;padding:.4rem .8rem;transform:translate(-50%, 0)}.aa-DetachedSearchButtonQuery{display:none}.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:.25rem;order:999}}.navbar.navbar-expand-sm #quarto-search,.navbar.navbar-expand-md #quarto-search{order:999}@media(min-width: 992px){.navbar .quarto-navbar-tools{margin-left:auto;order:900}}@media(max-width: 991.98px){#quarto-sidebar .sidebar-search{display:none}}#quarto-sidebar .sidebar-search .aa-Autocomplete{width:100%}.navbar .aa-Autocomplete .aa-Form{width:180px}.navbar #quarto-search.type-overlay .aa-Autocomplete{width:40px}.navbar #quarto-search.type-overlay .aa-Autocomplete .aa-Form{background-color:inherit;border:none}.navbar #quarto-search.type-overlay .aa-Autocomplete .aa-Form:focus-within{box-shadow:none;outline:none}.navbar #quarto-search.type-overlay .aa-Autocomplete .aa-Form .aa-InputWrapper{display:none}.navbar #quarto-search.type-overlay .aa-Autocomplete .aa-Form .aa-InputWrapper:focus-within{display:inherit}.navbar #quarto-search.type-overlay .aa-Autocomplete .aa-Form .aa-Label svg,.navbar #quarto-search.type-overlay .aa-Autocomplete .aa-Form .aa-LoadingIndicator svg{width:26px;height:26px;color:#fdfeff;opacity:1}.navbar #quarto-search.type-overlay .aa-Autocomplete svg.aa-SubmitIcon{width:26px;height:26px;color:#fdfeff;opacity:1}.aa-Autocomplete .aa-Form,.aa-DetachedFormContainer .aa-Form{align-items:center;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;color:#343a40;display:flex;line-height:1em;margin:0;position:relative;width:100%}.aa-Autocomplete .aa-Form:focus-within,.aa-DetachedFormContainer .aa-Form:focus-within{box-shadow:rgba(39,128,227,.6) 0 0 0 1px;outline:currentColor none medium}.aa-Autocomplete .aa-Form .aa-InputWrapperPrefix,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperPrefix{align-items:center;display:flex;flex-shrink:0;order:1}.aa-Autocomplete .aa-Form .aa-InputWrapperPrefix .aa-Label,.aa-Autocomplete .aa-Form .aa-InputWrapperPrefix .aa-LoadingIndicator,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperPrefix .aa-Label,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperPrefix .aa-LoadingIndicator{cursor:initial;flex-shrink:0;padding:0;text-align:left}.aa-Autocomplete .aa-Form .aa-InputWrapperPrefix .aa-Label svg,.aa-Autocomplete .aa-Form .aa-InputWrapperPrefix .aa-LoadingIndicator svg,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperPrefix .aa-Label svg,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperPrefix .aa-LoadingIndicator svg{color:#343a40;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:#343a40;font:inherit;height:calc(1.5em + .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:#343a40;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:#343a40;opacity:.8;cursor:pointer;display:flex;margin:0;width:calc(1.5em + .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:#343a40;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 + calc(1px * 2))}.aa-Autocomplete .aa-Form .aa-InputWrapperSuffix .aa-CopyButton,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperSuffix .aa-CopyButton{border:none;align-items:center;background:none;color:#343a40;opacity:.4;font-size:.7rem;cursor:pointer;display:none;margin:0;width:calc(1em + .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:#343a40;opacity:.8}.aa-Autocomplete .aa-Form .aa-InputWrapperSuffix .aa-CopyButton[hidden],.aa-DetachedFormContainer .aa-Form .aa-InputWrapperSuffix .aa-CopyButton[hidden]{display:none}.aa-PanelLayout:empty{display:none}.quarto-search-no-results.no-query{display:none}.aa-Source:has(.no-query){display:none}#quarto-search-results .aa-Panel{border:solid #dee2e6 1px}#quarto-search-results .aa-SourceNoResults{width:398px}.aa-DetachedOverlay .aa-Panel,#quarto-search-results .aa-Panel{max-height:65vh;overflow-y:auto;font-size:.925rem}.aa-DetachedOverlay .aa-SourceNoResults,#quarto-search-results .aa-SourceNoResults{height:60px;display:flex;justify-content:center;align-items:center}.aa-DetachedOverlay .search-error,#quarto-search-results .search-error{padding-top:10px;padding-left:20px;padding-right:20px;cursor:default}.aa-DetachedOverlay .search-error .search-error-title,#quarto-search-results .search-error .search-error-title{font-size:1.1rem;margin-bottom:.5rem}.aa-DetachedOverlay .search-error .search-error-title .search-error-icon,#quarto-search-results .search-error .search-error-title .search-error-icon{margin-right:8px}.aa-DetachedOverlay .search-error .search-error-text,#quarto-search-results .search-error .search-error-text{font-weight:300}.aa-DetachedOverlay .search-result-text,#quarto-search-results .search-result-text{font-weight:300;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;line-height:1.2rem;max-height:2.4rem}.aa-DetachedOverlay .aa-SourceHeader .search-result-header,#quarto-search-results .aa-SourceHeader .search-result-header{font-size:.875rem;background-color:#f2f2f2;padding-left:14px;padding-bottom:4px;padding-top:4px}.aa-DetachedOverlay .aa-SourceHeader .search-result-header-no-results,#quarto-search-results .aa-SourceHeader .search-result-header-no-results{display:none}.aa-DetachedOverlay .aa-SourceFooter .algolia-search-logo,#quarto-search-results .aa-SourceFooter .algolia-search-logo{width:110px;opacity:.85;margin:8px;float:right}.aa-DetachedOverlay .search-result-section,#quarto-search-results .search-result-section{font-size:.925em}.aa-DetachedOverlay a.search-result-link,#quarto-search-results a.search-result-link{color:inherit;text-decoration:none}.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item,#quarto-search-results li.aa-Item[aria-selected=true] .search-item{background-color:#2780e3}.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item.search-result-more,.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item .search-result-section,.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item .search-result-text,.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item .search-result-title-container,.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item .search-result-text-container,#quarto-search-results li.aa-Item[aria-selected=true] .search-item.search-result-more,#quarto-search-results li.aa-Item[aria-selected=true] .search-item .search-result-section,#quarto-search-results li.aa-Item[aria-selected=true] .search-item .search-result-text,#quarto-search-results li.aa-Item[aria-selected=true] .search-item .search-result-title-container,#quarto-search-results li.aa-Item[aria-selected=true] .search-item .search-result-text-container{color:#fff;background-color:#2780e3}.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item mark.search-match,.aa-DetachedOverlay li.aa-Item[aria-selected=true] .search-item .search-match.mark,#quarto-search-results li.aa-Item[aria-selected=true] .search-item mark.search-match,#quarto-search-results li.aa-Item[aria-selected=true] .search-item .search-match.mark{color:#fff;background-color:#4b95e8}.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item,#quarto-search-results li.aa-Item[aria-selected=false] .search-item{background-color:#fff}.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item.search-result-more,.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item .search-result-section,.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item .search-result-text,.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item .search-result-title-container,.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item .search-result-text-container,#quarto-search-results li.aa-Item[aria-selected=false] .search-item.search-result-more,#quarto-search-results li.aa-Item[aria-selected=false] .search-item .search-result-section,#quarto-search-results li.aa-Item[aria-selected=false] .search-item .search-result-text,#quarto-search-results li.aa-Item[aria-selected=false] .search-item .search-result-title-container,#quarto-search-results li.aa-Item[aria-selected=false] .search-item .search-result-text-container{color:#343a40}.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item mark.search-match,.aa-DetachedOverlay li.aa-Item[aria-selected=false] .search-item .search-match.mark,#quarto-search-results li.aa-Item[aria-selected=false] .search-item mark.search-match,#quarto-search-results li.aa-Item[aria-selected=false] .search-item .search-match.mark{color:inherit;background-color:#e5effc}.aa-DetachedOverlay .aa-Item .search-result-doc:not(.document-selectable) .search-result-title-container,#quarto-search-results .aa-Item .search-result-doc:not(.document-selectable) .search-result-title-container{background-color:#fff;color:#343a40}.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:#dee2e6;color:#343a40}.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:0em}.aa-DetachedOverlay .aa-Item .search-item .search-result-crumbs,#quarto-search-results .aa-Item .search-item .search-result-crumbs{white-space:nowrap;text-overflow:ellipsis;font-size:.8em;font-weight:300;margin-right:1em}.aa-DetachedOverlay .aa-Item .search-item .search-result-crumbs:not(.search-result-crumbs-wrap),#quarto-search-results .aa-Item .search-item .search-result-crumbs:not(.search-result-crumbs-wrap){max-width:30%;margin-left:auto;margin-top:.5em;margin-bottom:.1rem}.aa-DetachedOverlay .aa-Item .search-item .search-result-crumbs.search-result-crumbs-wrap,#quarto-search-results .aa-Item .search-item .search-result-crumbs.search-result-crumbs-wrap{flex-basis:100%;margin-top:0em;margin-bottom:.2em;margin-left:37px}.aa-DetachedOverlay .aa-Item .search-result-title-container,#quarto-search-results .aa-Item .search-result-title-container{font-size:1em;display:flex;flex-wrap:wrap;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:42px}.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 #dee2e6}.aa-DetachedSearchButton{background:none;border:none}.aa-DetachedSearchButton .aa-DetachedSearchButtonPlaceholder{display:none}.navbar .aa-DetachedSearchButton .aa-DetachedSearchButtonIcon{color:#fdfeff}.sidebar-tools-collapse #quarto-search,.sidebar-tools-main #quarto-search{display:inline}.sidebar-tools-collapse #quarto-search .aa-Autocomplete,.sidebar-tools-main #quarto-search .aa-Autocomplete{display:inline}.sidebar-tools-collapse #quarto-search .aa-DetachedSearchButton,.sidebar-tools-main #quarto-search .aa-DetachedSearchButton{padding-left:4px;padding-right:4px}.sidebar-tools-collapse #quarto-search .aa-DetachedSearchButton .aa-DetachedSearchButtonIcon,.sidebar-tools-main #quarto-search .aa-DetachedSearchButton .aa-DetachedSearchButtonIcon{color:#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(222,226,230,.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 #dee2e6;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:#343a40;cursor:pointer;margin:0 0 0 .5em;padding:0 .5em}.aa-DetachedCancelButton:hover,.aa-DetachedCancelButton:focus{box-shadow:rgba(39,128,227,.6) 0 0 0 1px;outline:currentColor none medium}.aa-DetachedContainer--modal{bottom:inherit;height:auto;margin:0 auto;position:absolute;top:100px;border-radius:6px;max-width:850px}@media(max-width: 575.98px){.aa-DetachedContainer--modal{width:100%;top:0px;border-radius:0px;border:none}}.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(52,58,64,.4);position:fixed;left:0;right:0;top:0;margin:0;padding:0;height:100vh;z-index:1100}.quarto-dashboard.nav-fixed.dashboard-sidebar #quarto-content.quarto-dashboard-content{padding:0em}.quarto-dashboard #quarto-content.quarto-dashboard-content{padding:1em}.quarto-dashboard #quarto-content.quarto-dashboard-content>*{padding-top:0}@media(min-width: 576px){.quarto-dashboard{height:100%}}.quarto-dashboard .card.valuebox.bslib-card.bg-primary{background-color:#5397e9 !important}.quarto-dashboard .card.valuebox.bslib-card.bg-secondary{background-color:#343a40 !important}.quarto-dashboard .card.valuebox.bslib-card.bg-success{background-color:#3aa716 !important}.quarto-dashboard .card.valuebox.bslib-card.bg-info{background-color:rgba(153,84,187,.7019607843) !important}.quarto-dashboard .card.valuebox.bslib-card.bg-warning{background-color:#fa6400 !important}.quarto-dashboard .card.valuebox.bslib-card.bg-danger{background-color:rgba(255,0,57,.7019607843) !important}.quarto-dashboard .card.valuebox.bslib-card.bg-light{background-color:#f8f9fa !important}.quarto-dashboard .card.valuebox.bslib-card.bg-dark{background-color:#343a40 !important}.quarto-dashboard.dashboard-fill{display:flex;flex-direction:column}.quarto-dashboard #quarto-appendix{display:none}.quarto-dashboard #quarto-header #quarto-dashboard-header{border-top:solid 1px #549be9;border-bottom:solid 1px #549be9}.quarto-dashboard #quarto-header #quarto-dashboard-header>nav{padding-left:1em;padding-right:1em}.quarto-dashboard #quarto-header #quarto-dashboard-header>nav .navbar-brand-container{padding-left:0}.quarto-dashboard #quarto-header #quarto-dashboard-header .navbar-toggler{margin-right:0}.quarto-dashboard #quarto-header #quarto-dashboard-header .navbar-toggler-icon{height:1em;width:1em;background-image:url('data:image/svg+xml,')}.quarto-dashboard #quarto-header #quarto-dashboard-header .navbar-brand-container{padding-right:1em}.quarto-dashboard #quarto-header #quarto-dashboard-header .navbar-title{font-size:1.1em}.quarto-dashboard #quarto-header #quarto-dashboard-header .navbar-nav{font-size:.9em}.quarto-dashboard #quarto-dashboard-header .navbar{padding:0}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-container{padding-left:1em}.quarto-dashboard #quarto-dashboard-header .navbar.slim .navbar-brand-container .nav-link,.quarto-dashboard #quarto-dashboard-header .navbar.slim .navbar-nav .nav-link{padding:.7em}.quarto-dashboard #quarto-dashboard-header .navbar .quarto-color-scheme-toggle{order:9}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-toggler{margin-left:.5em;order:10}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-nav .nav-link{padding:.5em;height:100%;display:flex;align-items:center}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-nav .active{background-color:#4b95e8}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-brand-container{padding:.5em .5em .5em 0;display:flex;flex-direction:row;margin-right:2em;align-items:center}@media(max-width: 767.98px){.quarto-dashboard #quarto-dashboard-header .navbar .navbar-brand-container{margin-right:auto}}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-collapse{align-self:stretch}@media(min-width: 768px){.quarto-dashboard #quarto-dashboard-header .navbar .navbar-collapse{order:8}}@media(max-width: 767.98px){.quarto-dashboard #quarto-dashboard-header .navbar .navbar-collapse{order:1000;padding-bottom:.5em}}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-collapse .navbar-nav{align-self:stretch}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-title{font-size:1.25em;line-height:1.1em;display:flex;flex-direction:row;flex-wrap:wrap;align-items:baseline}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-title .navbar-title-text{margin-right:.4em}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-title a{text-decoration:none;color:inherit}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-subtitle,.quarto-dashboard #quarto-dashboard-header .navbar .navbar-author{font-size:.9rem;margin-right:.5em}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-author{margin-left:auto}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-logo{max-height:48px;min-height:30px;object-fit:cover;margin-right:1em}.quarto-dashboard #quarto-dashboard-header .navbar .quarto-dashboard-links{order:9;padding-right:1em}.quarto-dashboard #quarto-dashboard-header .navbar .quarto-dashboard-link-text{margin-left:.25em}.quarto-dashboard #quarto-dashboard-header .navbar .quarto-dashboard-link{padding-right:0em;padding-left:.7em;text-decoration:none;color:#fdfeff}.quarto-dashboard .page-layout-custom .tab-content{padding:0;border:none}.quarto-dashboard-img-contain{height:100%;width:100%;object-fit:contain}@media(max-width: 575.98px){.quarto-dashboard .bslib-grid{grid-template-rows:minmax(1em, max-content) !important}.quarto-dashboard .sidebar-content{height:inherit}.quarto-dashboard .page-layout-custom{min-height:100vh}}.quarto-dashboard.dashboard-toolbar>.page-layout-custom,.quarto-dashboard.dashboard-sidebar>.page-layout-custom{padding:0}.quarto-dashboard .quarto-dashboard-content.quarto-dashboard-pages{padding:0}.quarto-dashboard .callout{margin-bottom:0;margin-top:0}.quarto-dashboard .html-fill-container figure{overflow:hidden}.quarto-dashboard bslib-tooltip .rounded-pill{border:solid #6c757d 1px}.quarto-dashboard bslib-tooltip .rounded-pill .svg{fill:#343a40}.quarto-dashboard .tabset .dashboard-card-no-title .nav-tabs{margin-left:0;margin-right:auto}.quarto-dashboard .tabset .tab-content{border:none}.quarto-dashboard .tabset .card-header .nav-link[role=tab]{margin-top:-6px;padding-top:6px;padding-bottom:6px}.quarto-dashboard .card.valuebox,.quarto-dashboard .card.bslib-value-box{min-height:3rem}.quarto-dashboard .card.valuebox .card-body,.quarto-dashboard .card.bslib-value-box .card-body{padding:0}.quarto-dashboard .bslib-value-box .value-box-value{font-size:clamp(.1em,15cqw,5em)}.quarto-dashboard .bslib-value-box .value-box-showcase .bi{font-size:clamp(.1em,max(18cqw,5.2cqh),5em);text-align:center;height:1em}.quarto-dashboard .bslib-value-box .value-box-showcase .bi::before{vertical-align:1em}.quarto-dashboard .bslib-value-box .value-box-area{margin-top:auto;margin-bottom:auto}.quarto-dashboard .card figure.quarto-float{display:flex;flex-direction:column;align-items:center}.quarto-dashboard .dashboard-scrolling{padding:1em}.quarto-dashboard .full-height{height:100%}.quarto-dashboard .showcase-bottom .value-box-grid{display:grid;grid-template-columns:1fr;grid-template-rows:1fr auto;grid-template-areas:"top" "bottom"}.quarto-dashboard .showcase-bottom .value-box-grid .value-box-showcase{grid-area:bottom;padding:0;margin:0}.quarto-dashboard .showcase-bottom .value-box-grid .value-box-showcase i.bi{font-size:4rem}.quarto-dashboard .showcase-bottom .value-box-grid .value-box-area{grid-area:top}.quarto-dashboard .tab-content{margin-bottom:0}.quarto-dashboard .bslib-card .bslib-navs-card-title{justify-content:stretch;align-items:end}.quarto-dashboard .card-header{display:flex;flex-wrap:wrap;justify-content:space-between}.quarto-dashboard .card-header .card-title{display:flex;flex-direction:column;justify-content:center;margin-bottom:0}.quarto-dashboard .tabset .card-toolbar{margin-bottom:1em}.quarto-dashboard .bslib-grid>.bslib-sidebar-layout{border:none;gap:var(--bslib-spacer, 1rem)}.quarto-dashboard .bslib-grid>.bslib-sidebar-layout>.main{padding:0}.quarto-dashboard .bslib-grid>.bslib-sidebar-layout>.sidebar{border-radius:.25rem;border:1px solid rgba(0,0,0,.175)}.quarto-dashboard .bslib-grid>.bslib-sidebar-layout>.collapse-toggle{display:none}@media(max-width: 767.98px){.quarto-dashboard .bslib-grid>.bslib-sidebar-layout{grid-template-columns:1fr;grid-template-rows:max-content 1fr}.quarto-dashboard .bslib-grid>.bslib-sidebar-layout>.main{grid-column:1;grid-row:2}.quarto-dashboard .bslib-grid>.bslib-sidebar-layout .sidebar{grid-column:1;grid-row:1}}.quarto-dashboard .sidebar-right .sidebar{padding-left:2.5em}.quarto-dashboard .sidebar-right .collapse-toggle{left:2px}.quarto-dashboard .quarto-dashboard .sidebar-right button.collapse-toggle:not(.transitioning){left:unset}.quarto-dashboard aside.sidebar{padding-left:1em;padding-right:1em;background-color:rgba(52,58,64,.25);color:#343a40}.quarto-dashboard .bslib-sidebar-layout>div.main{padding:.7em}.quarto-dashboard .bslib-sidebar-layout button.collapse-toggle{margin-top:.3em}.quarto-dashboard .bslib-sidebar-layout .collapse-toggle{top:0}.quarto-dashboard .bslib-sidebar-layout.sidebar-collapsed:not(.transitioning):not(.sidebar-right) .collapse-toggle{left:2px}.quarto-dashboard .sidebar>section>.h3:first-of-type{margin-top:0em}.quarto-dashboard .sidebar .h3,.quarto-dashboard .sidebar .h4,.quarto-dashboard .sidebar .h5,.quarto-dashboard .sidebar .h6{margin-top:.5em}.quarto-dashboard .sidebar form{flex-direction:column;align-items:start;margin-bottom:1em}.quarto-dashboard .sidebar form div[class*=oi-][class$=-input]{flex-direction:column}.quarto-dashboard .sidebar form[class*=oi-][class$=-toggle]{flex-direction:row-reverse;align-items:center;justify-content:start}.quarto-dashboard .sidebar form input[type=range]{margin-top:.5em;margin-right:.8em;margin-left:1em}.quarto-dashboard .sidebar label{width:fit-content}.quarto-dashboard .sidebar .card-body{margin-bottom:2em}.quarto-dashboard .sidebar .shiny-input-container{margin-bottom:1em}.quarto-dashboard .sidebar .shiny-options-group{margin-top:0}.quarto-dashboard .sidebar .control-label{margin-bottom:.3em}.quarto-dashboard .card .card-body .quarto-layout-row{align-items:stretch}.quarto-dashboard .toolbar{font-size:.9em;display:flex;flex-direction:row;border-top:solid 1px #bcbfc0;padding:1em;flex-wrap:wrap;background-color:rgba(52,58,64,.25)}.quarto-dashboard .toolbar .cell-output-display{display:flex}.quarto-dashboard .toolbar .shiny-input-container{padding-bottom:.5em;margin-bottom:.5em;width:inherit}.quarto-dashboard .toolbar .shiny-input-container>.checkbox:first-child{margin-top:6px}.quarto-dashboard .toolbar>*:last-child{margin-right:0}.quarto-dashboard .toolbar>*>*{margin-right:1em;align-items:baseline}.quarto-dashboard .toolbar>*>*>a{text-decoration:none;margin-top:auto;margin-bottom:auto}.quarto-dashboard .toolbar .shiny-input-container{padding-bottom:0;margin-bottom:0}.quarto-dashboard .toolbar .shiny-input-container>*{flex-shrink:0;flex-grow:0}.quarto-dashboard .toolbar .form-group.shiny-input-container:not([role=group])>label{margin-bottom:0}.quarto-dashboard .toolbar .shiny-input-container.no-baseline{align-items:start;padding-top:6px}.quarto-dashboard .toolbar .shiny-input-container{display:flex;align-items:baseline}.quarto-dashboard .toolbar .shiny-input-container label{padding-right:.4em}.quarto-dashboard .toolbar .shiny-input-container .bslib-input-switch{margin-top:6px}.quarto-dashboard .toolbar input[type=text]{line-height:1;width:inherit}.quarto-dashboard .toolbar .input-daterange{width:inherit}.quarto-dashboard .toolbar .input-daterange input[type=text]{height:2.4em;width:10em}.quarto-dashboard .toolbar .input-daterange .input-group-addon{height:auto;padding:0;margin-left:-5px !important;margin-right:-5px}.quarto-dashboard .toolbar .input-daterange .input-group-addon .input-group-text{padding-top:0;padding-bottom:0;height:100%}.quarto-dashboard .toolbar span.irs.irs--shiny{width:10em}.quarto-dashboard .toolbar span.irs.irs--shiny .irs-line{top:9px}.quarto-dashboard .toolbar span.irs.irs--shiny .irs-min,.quarto-dashboard .toolbar span.irs.irs--shiny .irs-max,.quarto-dashboard .toolbar span.irs.irs--shiny .irs-from,.quarto-dashboard .toolbar span.irs.irs--shiny .irs-to,.quarto-dashboard .toolbar span.irs.irs--shiny .irs-single{top:20px}.quarto-dashboard .toolbar span.irs.irs--shiny .irs-bar{top:8px}.quarto-dashboard .toolbar span.irs.irs--shiny .irs-handle{top:0px}.quarto-dashboard .toolbar .shiny-input-checkboxgroup>label{margin-top:6px}.quarto-dashboard .toolbar .shiny-input-checkboxgroup>.shiny-options-group{margin-top:0;align-items:baseline}.quarto-dashboard .toolbar .shiny-input-radiogroup>label{margin-top:6px}.quarto-dashboard .toolbar .shiny-input-radiogroup>.shiny-options-group{align-items:baseline;margin-top:0}.quarto-dashboard .toolbar .shiny-input-radiogroup>.shiny-options-group>.radio{margin-right:.3em}.quarto-dashboard .toolbar .form-select{padding-top:.2em;padding-bottom:.2em}.quarto-dashboard .toolbar .shiny-input-select{min-width:6em}.quarto-dashboard .toolbar div.checkbox{margin-bottom:0px}.quarto-dashboard .toolbar>.checkbox:first-child{margin-top:6px}.quarto-dashboard .toolbar form{width:fit-content}.quarto-dashboard .toolbar form label{padding-top:.2em;padding-bottom:.2em;width:fit-content}.quarto-dashboard .toolbar form input[type=date]{width:fit-content}.quarto-dashboard .toolbar form input[type=color]{width:3em}.quarto-dashboard .toolbar form button{padding:.4em}.quarto-dashboard .toolbar form select{width:fit-content}.quarto-dashboard .toolbar>*{font-size:.9em;flex-grow:0}.quarto-dashboard .toolbar .shiny-input-container label{margin-bottom:1px}.quarto-dashboard .toolbar-bottom{margin-top:1em;margin-bottom:0 !important;order:2}.quarto-dashboard .quarto-dashboard-content>.dashboard-toolbar-container>.toolbar-content>.tab-content>.tab-pane>*:not(.bslib-sidebar-layout){padding:1em}.quarto-dashboard .quarto-dashboard-content>.dashboard-toolbar-container>.toolbar-content>*:not(.tab-content){padding:1em}.quarto-dashboard .quarto-dashboard-content>.tab-content>.dashboard-page>.dashboard-toolbar-container>.toolbar-content,.quarto-dashboard .quarto-dashboard-content>.tab-content>.dashboard-page:not(.dashboard-sidebar-container)>*:not(.dashboard-toolbar-container){padding:1em}.quarto-dashboard .toolbar-content{padding:0}.quarto-dashboard .quarto-dashboard-content.quarto-dashboard-pages .tab-pane>.dashboard-toolbar-container .toolbar{border-radius:0;margin-bottom:0}.quarto-dashboard .dashboard-toolbar-container.toolbar-toplevel .toolbar{border-bottom:1px solid rgba(0,0,0,.175)}.quarto-dashboard .dashboard-toolbar-container.toolbar-toplevel .toolbar-bottom{margin-top:0}.quarto-dashboard .dashboard-toolbar-container:not(.toolbar-toplevel) .toolbar{margin-bottom:1em;border-top:none;border-radius:.25rem;border:1px solid rgba(0,0,0,.175)}.quarto-dashboard .vega-embed.has-actions details{width:1.7em;height:2em;position:absolute !important;top:0;right:0}.quarto-dashboard .dashboard-toolbar-container{padding:0}.quarto-dashboard .card .card-header p:last-child,.quarto-dashboard .card .card-footer p:last-child{margin-bottom:0}.quarto-dashboard .card .card-body>.h4:first-child{margin-top:0}.quarto-dashboard .card .card-body{z-index:1000}@media(max-width: 767.98px){.quarto-dashboard .card .card-body .itables div.dataTables_wrapper div.dataTables_length,.quarto-dashboard .card .card-body .itables div.dataTables_wrapper div.dataTables_info,.quarto-dashboard .card .card-body .itables div.dataTables_wrapper div.dataTables_paginate{text-align:initial}.quarto-dashboard .card .card-body .itables div.dataTables_wrapper div.dataTables_filter{text-align:right}.quarto-dashboard .card .card-body .itables div.dataTables_wrapper div.dataTables_paginate ul.pagination{justify-content:initial}}.quarto-dashboard .card .card-body .itables .dataTables_wrapper{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;padding-top:0}.quarto-dashboard .card .card-body .itables .dataTables_wrapper table{flex-shrink:0}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dt-buttons{margin-bottom:.5em;margin-left:auto;width:fit-content;float:right}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dt-buttons.btn-group{background:#fff;border:none}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dt-buttons .btn-secondary{background-color:#fff;background-image:none;border:solid #dee2e6 1px;padding:.2em .7em}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dt-buttons .btn span{font-size:.8em;color:#343a40}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_info{margin-left:.5em;margin-bottom:.5em;padding-top:0}@media(min-width: 768px){.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_info{font-size:.875em}}@media(max-width: 767.98px){.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_info{font-size:.8em}}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_filter{margin-bottom:.5em;font-size:.875em}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_filter input[type=search]{padding:1px 5px 1px 5px;font-size:.875em}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_length{flex-basis:1 1 50%;margin-bottom:.5em;font-size:.875em}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_length select{padding:.4em 3em .4em .5em;font-size:.875em;margin-left:.2em;margin-right:.2em}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_paginate{flex-shrink:0}@media(min-width: 768px){.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_paginate{margin-left:auto}}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_paginate ul.pagination .paginate_button .page-link{font-size:.8em}.quarto-dashboard .card .card-footer{font-size:.9em}.quarto-dashboard .card .card-toolbar{display:flex;flex-grow:1;flex-direction:row;width:100%;flex-wrap:wrap}.quarto-dashboard .card .card-toolbar>*{font-size:.8em;flex-grow:0}.quarto-dashboard .card .card-toolbar>.card-title{font-size:1em;flex-grow:1;align-self:flex-start;margin-top:.1em}.quarto-dashboard .card .card-toolbar .cell-output-display{display:flex}.quarto-dashboard .card .card-toolbar .shiny-input-container{padding-bottom:.5em;margin-bottom:.5em;width:inherit}.quarto-dashboard .card .card-toolbar .shiny-input-container>.checkbox:first-child{margin-top:6px}.quarto-dashboard .card .card-toolbar>*:last-child{margin-right:0}.quarto-dashboard .card .card-toolbar>*>*{margin-right:1em;align-items:baseline}.quarto-dashboard .card .card-toolbar>*>*>a{text-decoration:none;margin-top:auto;margin-bottom:auto}.quarto-dashboard .card .card-toolbar form{width:fit-content}.quarto-dashboard .card .card-toolbar form label{padding-top:.2em;padding-bottom:.2em;width:fit-content}.quarto-dashboard .card .card-toolbar form input[type=date]{width:fit-content}.quarto-dashboard .card .card-toolbar form input[type=color]{width:3em}.quarto-dashboard .card .card-toolbar form button{padding:.4em}.quarto-dashboard .card .card-toolbar form select{width:fit-content}.quarto-dashboard .card .card-toolbar .cell-output-display{display:flex}.quarto-dashboard .card .card-toolbar .shiny-input-container{padding-bottom:.5em;margin-bottom:.5em;width:inherit}.quarto-dashboard .card .card-toolbar .shiny-input-container>.checkbox:first-child{margin-top:6px}.quarto-dashboard .card .card-toolbar>*:last-child{margin-right:0}.quarto-dashboard .card .card-toolbar>*>*{margin-right:1em;align-items:baseline}.quarto-dashboard .card .card-toolbar>*>*>a{text-decoration:none;margin-top:auto;margin-bottom:auto}.quarto-dashboard .card .card-toolbar .shiny-input-container{padding-bottom:0;margin-bottom:0}.quarto-dashboard .card .card-toolbar .shiny-input-container>*{flex-shrink:0;flex-grow:0}.quarto-dashboard .card .card-toolbar .form-group.shiny-input-container:not([role=group])>label{margin-bottom:0}.quarto-dashboard .card .card-toolbar .shiny-input-container.no-baseline{align-items:start;padding-top:6px}.quarto-dashboard .card .card-toolbar .shiny-input-container{display:flex;align-items:baseline}.quarto-dashboard .card .card-toolbar .shiny-input-container label{padding-right:.4em}.quarto-dashboard .card .card-toolbar .shiny-input-container .bslib-input-switch{margin-top:6px}.quarto-dashboard .card .card-toolbar input[type=text]{line-height:1;width:inherit}.quarto-dashboard .card .card-toolbar .input-daterange{width:inherit}.quarto-dashboard .card .card-toolbar .input-daterange input[type=text]{height:2.4em;width:10em}.quarto-dashboard .card .card-toolbar .input-daterange .input-group-addon{height:auto;padding:0;margin-left:-5px !important;margin-right:-5px}.quarto-dashboard .card .card-toolbar .input-daterange .input-group-addon .input-group-text{padding-top:0;padding-bottom:0;height:100%}.quarto-dashboard .card .card-toolbar span.irs.irs--shiny{width:10em}.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-line{top:9px}.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-min,.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-max,.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-from,.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-to,.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-single{top:20px}.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-bar{top:8px}.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-handle{top:0px}.quarto-dashboard .card .card-toolbar .shiny-input-checkboxgroup>label{margin-top:6px}.quarto-dashboard .card .card-toolbar .shiny-input-checkboxgroup>.shiny-options-group{margin-top:0;align-items:baseline}.quarto-dashboard .card .card-toolbar .shiny-input-radiogroup>label{margin-top:6px}.quarto-dashboard .card .card-toolbar .shiny-input-radiogroup>.shiny-options-group{align-items:baseline;margin-top:0}.quarto-dashboard .card .card-toolbar .shiny-input-radiogroup>.shiny-options-group>.radio{margin-right:.3em}.quarto-dashboard .card .card-toolbar .form-select{padding-top:.2em;padding-bottom:.2em}.quarto-dashboard .card .card-toolbar .shiny-input-select{min-width:6em}.quarto-dashboard .card .card-toolbar div.checkbox{margin-bottom:0px}.quarto-dashboard .card .card-toolbar>.checkbox:first-child{margin-top:6px}.quarto-dashboard .card-body>table>thead{border-top:none}.quarto-dashboard .card-body>.table>:not(caption)>*>*{background-color:#fff}.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:#fff;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 p.card-img-top>img{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 .listing-item-img-placeholder{background-color:rgba(52,58,64,.25);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;word-break:keep-all}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 .body pre code{white-space:pre-wrap}div.quarto-post a{color:#343a40;text-decoration:none}div.quarto-post .metadata{display:flex;flex-direction:column;font-size:.8em;font-family:"Source Sans Pro",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";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:#626d78;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:#2761e3}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:#626d78;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:#2761e3}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:#626d78;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:#2761e3}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:#626d78;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:#2761e3}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:#626d78;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:#2761e3}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;border:solid 1px #dee2e6;border-radius:.25rem;color:#343a40;font-size:.875rem}.tippy-box[data-theme~=quarto]>.tippy-backdrop{background-color:#fff}.tippy-box[data-theme~=quarto]>.tippy-arrow:after,.tippy-box[data-theme~=quarto]>.tippy-svg-arrow:after{content:"";position:absolute;z-index:-1}.tippy-box[data-theme~=quarto]>.tippy-arrow:after{border-color:rgba(0,0,0,0);border-style:solid}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-6px}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-6px}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-6px}.tippy-box[data-placement^=left]>.tippy-arrow:before{right:-6px}.tippy-box[data-theme~=quarto][data-placement^=top]>.tippy-arrow:before{border-top-color:#fff}.tippy-box[data-theme~=quarto][data-placement^=top]>.tippy-arrow:after{border-top-color:#dee2e6;border-width:7px 7px 0;top:17px;left:1px}.tippy-box[data-theme~=quarto][data-placement^=top]>.tippy-svg-arrow>svg{top:16px}.tippy-box[data-theme~=quarto][data-placement^=top]>.tippy-svg-arrow:after{top:17px}.tippy-box[data-theme~=quarto][data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:#fff;bottom:16px}.tippy-box[data-theme~=quarto][data-placement^=bottom]>.tippy-arrow:after{border-bottom-color:#dee2e6;border-width:0 7px 7px;bottom:17px;left:1px}.tippy-box[data-theme~=quarto][data-placement^=bottom]>.tippy-svg-arrow>svg{bottom:15px}.tippy-box[data-theme~=quarto][data-placement^=bottom]>.tippy-svg-arrow:after{bottom:17px}.tippy-box[data-theme~=quarto][data-placement^=left]>.tippy-arrow:before{border-left-color:#fff}.tippy-box[data-theme~=quarto][data-placement^=left]>.tippy-arrow:after{border-left-color:#dee2e6;border-width:7px 0 7px 7px;left:17px;top:1px}.tippy-box[data-theme~=quarto][data-placement^=left]>.tippy-svg-arrow>svg{left:11px}.tippy-box[data-theme~=quarto][data-placement^=left]>.tippy-svg-arrow:after{left:12px}.tippy-box[data-theme~=quarto][data-placement^=right]>.tippy-arrow:before{border-right-color:#fff;right:16px}.tippy-box[data-theme~=quarto][data-placement^=right]>.tippy-arrow:after{border-width:7px 7px 7px 0;right:17px;top:1px;border-right-color:#dee2e6}.tippy-box[data-theme~=quarto][data-placement^=right]>.tippy-svg-arrow>svg{right:11px}.tippy-box[data-theme~=quarto][data-placement^=right]>.tippy-svg-arrow:after{right:12px}.tippy-box[data-theme~=quarto]>.tippy-svg-arrow{fill:#343a40}.tippy-box[data-theme~=quarto]>.tippy-svg-arrow:after{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMCA2czEuNzk2LS4wMTMgNC42Ny0zLjYxNUM1Ljg1MS45IDYuOTMuMDA2IDggMGMxLjA3LS4wMDYgMi4xNDguODg3IDMuMzQzIDIuMzg1QzE0LjIzMyA2LjAwNSAxNiA2IDE2IDZIMHoiIGZpbGw9InJnYmEoMCwgOCwgMTYsIDAuMikiLz48L3N2Zz4=);background-size:16px 6px;width:16px;height:6px}.top-right{position:absolute;top:1em;right:1em}.visually-hidden{border:0;clip:rect(0 0 0 0);height:auto;margin:0;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap}.hidden{display:none !important}.zindex-bottom{z-index:-1 !important}figure.figure{display:block}.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: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,.quarto-figure-left>figure>div{text-align:left}.quarto-figure-center>figure>p,.quarto-figure-center>figure>div{text-align:center}.quarto-figure-right>figure>p,.quarto-figure-right>figure>div{text-align:right}.quarto-figure>figure>div.cell-annotation,.quarto-figure>figure>div code{text-align:left}figure>p:empty{display:none}figure>p:first-child{margin-top:0;margin-bottom:0}figure>figcaption.quarto-float-caption-bottom{margin-bottom:.5em}figure>figcaption.quarto-float-caption-top{margin-top:.5em}div[id^=tbl-]{position:relative}.quarto-figure>.anchorjs-link{position:absolute;top:.6em;right:.5em}div[id^=tbl-]>.anchorjs-link{position:absolute;top:.7em;right:.3em}.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.table{margin-top:.5rem;margin-bottom:.5rem}caption,.table-caption{padding-top:.5rem;padding-bottom:.5rem;text-align:center}figure.quarto-float-tbl figcaption.quarto-float-caption-top{margin-top:.5rem;margin-bottom:.25rem;text-align:center}figure.quarto-float-tbl figcaption.quarto-float-caption-bottom{padding-top:.25rem;margin-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}.footnote-back{margin-left:.2em}.tippy-content{overflow-x:auto}.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),.knitsql-table: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}a{text-underline-offset:3px}div.ansi-escaped-output{font-family:monospace;display:block}/*! +* +* ansi colors from IPython notebook's +* +* we also add `bright-[color]-` synonyms for the `-[color]-intense` classes since +* that seems to be what ansi_up emits +* +*/.ansi-black-fg{color:#3e424d}.ansi-black-bg{background-color:#3e424d}.ansi-black-intense-black,.ansi-bright-black-fg{color:#282c36}.ansi-black-intense-black,.ansi-bright-black-bg{background-color:#282c36}.ansi-red-fg{color:#e75c58}.ansi-red-bg{background-color:#e75c58}.ansi-red-intense-red,.ansi-bright-red-fg{color:#b22b31}.ansi-red-intense-red,.ansi-bright-red-bg{background-color:#b22b31}.ansi-green-fg{color:#00a250}.ansi-green-bg{background-color:#00a250}.ansi-green-intense-green,.ansi-bright-green-fg{color:#007427}.ansi-green-intense-green,.ansi-bright-green-bg{background-color:#007427}.ansi-yellow-fg{color:#ddb62b}.ansi-yellow-bg{background-color:#ddb62b}.ansi-yellow-intense-yellow,.ansi-bright-yellow-fg{color:#b27d12}.ansi-yellow-intense-yellow,.ansi-bright-yellow-bg{background-color:#b27d12}.ansi-blue-fg{color:#208ffb}.ansi-blue-bg{background-color:#208ffb}.ansi-blue-intense-blue,.ansi-bright-blue-fg{color:#0065ca}.ansi-blue-intense-blue,.ansi-bright-blue-bg{background-color:#0065ca}.ansi-magenta-fg{color:#d160c4}.ansi-magenta-bg{background-color:#d160c4}.ansi-magenta-intense-magenta,.ansi-bright-magenta-fg{color:#a03196}.ansi-magenta-intense-magenta,.ansi-bright-magenta-bg{background-color:#a03196}.ansi-cyan-fg{color:#60c6c8}.ansi-cyan-bg{background-color:#60c6c8}.ansi-cyan-intense-cyan,.ansi-bright-cyan-fg{color:#258f8f}.ansi-cyan-intense-cyan,.ansi-bright-cyan-bg{background-color:#258f8f}.ansi-white-fg{color:#c5c1b4}.ansi-white-bg{background-color:#c5c1b4}.ansi-white-intense-white,.ansi-bright-white-fg{color:#a1a6b2}.ansi-white-intense-white,.ansi-bright-white-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: #343a40;--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:rgba(0,0,0,0);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:rgba(0,0,0,0);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:rgba(0,0,0,0)}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%}.code-annotation-tip-content{word-wrap:break-word}.code-annotation-container-hidden{display:none !important}dl.code-annotation-container-grid{display:grid;grid-template-columns:min-content auto}dl.code-annotation-container-grid dt{grid-column:1}dl.code-annotation-container-grid dd{grid-column:2}pre.sourceCode.code-annotation-code{padding-right:0}code.sourceCode .code-annotation-anchor{z-index:100;position:relative;float:right;background-color:rgba(0,0,0,0)}input[type=checkbox]{margin-right:.5ch}:root{--mermaid-bg-color: #fff;--mermaid-edge-color: #343a40;--mermaid-node-fg-color: #343a40;--mermaid-fg-color: #343a40;--mermaid-fg-color--lighter: #4b545c;--mermaid-fg-color--lightest: #626d78;--mermaid-font-family: Source Sans Pro, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;--mermaid-label-bg-color: #fff;--mermaid-label-fg-color: #2780e3;--mermaid-node-bg-color: rgba(39, 128, 227, 0.1);--mermaid-node-fg-color: #343a40}@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:rgba(0,0,0,0);z-index:3}.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}ul>li:not(:has(>p))>ul,ol>li:not(:has(>p))>ul,ul>li:not(:has(>p))>ol,ol>li:not(:has(>p))>ol{margin-bottom:0}ul>li:not(:has(>p))>ul>li:has(>p),ol>li:not(:has(>p))>ul>li:has(>p),ul>li:not(:has(>p))>ol>li:has(>p),ol>li:not(:has(>p))>ol>li:has(>p){margin-top:1rem}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}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(850px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [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] minmax(50px, 100px) [page-start-inset] 50px [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(500px, calc(850px - 3em)) [body-content-end] 3em [body-end] 50px [body-end-outset] minmax(0px, 250px) [page-end-inset] minmax(50px, 100px) [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(800px - 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(800px - 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(50px, 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, 200px) [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(800px - 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(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.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(800px - 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(1250px - 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(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: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(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.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] 1.5em [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] 4fr [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] 50px [body-end-outset] minmax(25px, 50px) [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 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] 4fr [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 .page-columns.page-full>*,.page-columns.column-page-left>*{grid-column:page-start/body-content-end}.page-columns.column-page-right .page-columns.page-full>*,.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;opacity:.999}.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;opacity:.999}.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;opacity:.999}.page-columns .column-body-outset-right table{background:#fff}.page-columns .column-page{grid-column:page-start/page-end;z-index:998;opacity:.999}.page-columns .column-page table{background:#fff}.page-columns .column-page-inset{grid-column:page-start-inset/page-end-inset;z-index:998;opacity:.999}.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;opacity:.999}.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;opacity:.999}.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;opacity:.999}.page-columns .column-page-left table{background:#fff}.page-columns .column-page-right{grid-column:body-content-start/page-end;z-index:998;opacity:.999}.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,#quarto-content.page-columns #quarto-margin-sidebar.collapsing,#quarto-content.page-columns #quarto-sidebar.collapsing{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;opacity:.999}.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;opacity:.999}.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;opacity:.999}.page-columns .column-screen-inset-right table{background:#fff}.page-columns .column-screen{grid-column:screen-start/screen-end;z-index:998;opacity:.999}.page-columns .column-screen table{background:#fff}.page-columns .column-screen-left{grid-column:screen-start/body-content-end;z-index:998;opacity:.999}.page-columns .column-screen-left table{background:#fff}.page-columns .column-screen-right{grid-column:body-content-start/screen-end;z-index:998;opacity:.999}.page-columns .column-screen-right table{background:#fff}.page-columns .column-screen-inset-shaded{grid-column:screen-start/screen-end;padding:1em;background:#f8f9fa;z-index:998;opacity:.999;margin-bottom:1em}.zindex-content{z-index:998;opacity:.999}.zindex-modal{z-index:1055;opacity:.999}.zindex-over-content{z-index:999;opacity:.999}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:not(.footnotes):not(.sidebar),.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:not(.footnotes):not(.sidebar),.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:not(.footnotes):not(.sidebar),.page-columns.toc-left .column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;opacity:.999}.page-columns.toc-left .no-row-height{height:initial;overflow:initial}}@media(max-width: 767.98px){.margin-caption,div.aside,aside:not(.footnotes):not(.sidebar),.column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;opacity:.999}.no-row-height{height:initial;overflow:initial}#quarto-margin-sidebar{display:none}#quarto-sidebar-toc-left{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{color:inherit;margin-top:2rem;margin-bottom:1rem;font-weight:600}h1.title,.title.h1{margin-top:0}main.content>section:first-of-type>h2:first-child,main.content>section:first-of-type>.h2:first-child{margin-top:0}h2,.h2{border-bottom:1px solid #dee2e6;padding-bottom:.5rem}h3,.h3{font-weight:600}h3,.h3,h4,.h4{opacity:.9;margin-top:1.5rem}h5,.h5,h6,.h6{opacity:.9}.header-section-number{color:#6d7a86}.nav-link.active .header-section-number{color:inherit}mark,.mark{padding:0em}.panel-caption,.figure-caption,.subfigure-caption,.table-caption,figcaption,caption{font-size:.9rem;color:#6d7a86}.quarto-layout-cell[data-ref-parent] caption{color:#6d7a86}.column-margin figcaption,.margin-caption,div.aside,aside,.column-margin{color:#6d7a86;font-size:.825rem}.panel-caption.margin-caption{text-align:inherit}.column-margin.column-container p{margin-bottom:0}.column-margin.column-container>*:not(.collapse):first-child{padding-bottom:.5em;display:block}.column-margin.column-container>*:not(.collapse):not(:first-child){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}}.nav-tabs .nav-item{margin-top:1px;cursor:pointer}.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:0}.tab-pane>p:nth-child(1){padding-top:0}.tab-pane>p:last-child{margin-bottom:0}.tab-pane>pre:last-child{margin-bottom:0}.tab-content>.tab-pane:not(.active){display:none !important}div.sourceCode{background-color:rgba(233,236,239,.65);border:1px solid rgba(233,236,239,.65);border-radius:.25rem}pre.sourceCode{background-color:rgba(0,0,0,0)}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:#6d7a86}.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 pre code:not(.sourceCode),li pre code:not(.sourceCode),pre code:not(.sourceCode){background-color:initial}p code:not(.sourceCode),li code:not(.sourceCode),td code:not(.sourceCode){background-color:#f8f9fa;padding:.2em}nav p code:not(.sourceCode),nav li code:not(.sourceCode),nav td code:not(.sourceCode){background-color:rgba(0,0,0,0);padding:0}td code:not(.sourceCode){white-space:pre-wrap}#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:rgba(0,0,0,0);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.quarto-banner-title-block-sidebar>*{padding-top:1.65em}figure .quarto-notebook-link{margin-top:.5em}.quarto-notebook-link{font-size:.75em;color:#6c757d;margin-bottom:1em;text-decoration:none;display:block}.quarto-notebook-link:hover{text-decoration:underline;color:#2761e3}.quarto-notebook-link::before{display:inline-block;height:.75rem;width:.75rem;margin-bottom:0em;margin-right:.25em;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:.75rem .75rem}.toc-actions i.bi,.quarto-code-links i.bi,.quarto-other-links i.bi,.quarto-alternate-notebooks i.bi,.quarto-alternate-formats i.bi{margin-right:.4em;font-size:.8rem}.quarto-other-links-text-target .quarto-code-links i.bi,.quarto-other-links-text-target .quarto-other-links i.bi{margin-right:.2em}.quarto-other-formats-text-target .quarto-alternate-formats i.bi{margin-right:.1em}.toc-actions i.bi.empty,.quarto-code-links i.bi.empty,.quarto-other-links i.bi.empty,.quarto-alternate-notebooks i.bi.empty,.quarto-alternate-formats i.bi.empty{padding-left:1em}.quarto-notebook h2,.quarto-notebook .h2{border-bottom:none}.quarto-notebook .cell-container{display:flex}.quarto-notebook .cell-container .cell{flex-grow:4}.quarto-notebook .cell-container .cell-decorator{padding-top:1.5em;padding-right:1em;text-align:right}.quarto-notebook .cell-container.code-fold .cell-decorator{padding-top:3em}.quarto-notebook .cell-code code{white-space:pre-wrap}.quarto-notebook .cell .cell-output-stderr pre code,.quarto-notebook .cell .cell-output-stdout pre code{white-space:pre-wrap;overflow-wrap:anywhere}.toc-actions,.quarto-alternate-formats,.quarto-other-links,.quarto-code-links,.quarto-alternate-notebooks{padding-left:0em}.sidebar .toc-actions a,.sidebar .quarto-alternate-formats a,.sidebar .quarto-other-links a,.sidebar .quarto-code-links a,.sidebar .quarto-alternate-notebooks a,.sidebar nav[role=doc-toc] a{text-decoration:none}.sidebar .toc-actions a:hover,.sidebar .quarto-other-links a:hover,.sidebar .quarto-code-links a:hover,.sidebar .quarto-alternate-formats a:hover,.sidebar .quarto-alternate-notebooks a:hover{color:#2761e3}.sidebar .toc-actions h2,.sidebar .toc-actions .h2,.sidebar .quarto-code-links h2,.sidebar .quarto-code-links .h2,.sidebar .quarto-other-links h2,.sidebar .quarto-other-links .h2,.sidebar .quarto-alternate-notebooks h2,.sidebar .quarto-alternate-notebooks .h2,.sidebar .quarto-alternate-formats h2,.sidebar .quarto-alternate-formats .h2,.sidebar nav[role=doc-toc]>h2,.sidebar nav[role=doc-toc]>.h2{font-weight:500;margin-bottom:.2rem;margin-top:.3rem;font-family:inherit;border-bottom:0;padding-bottom:0;padding-top:0px}.sidebar .toc-actions>h2,.sidebar .toc-actions>.h2,.sidebar .quarto-code-links>h2,.sidebar .quarto-code-links>.h2,.sidebar .quarto-other-links>h2,.sidebar .quarto-other-links>.h2,.sidebar .quarto-alternate-notebooks>h2,.sidebar .quarto-alternate-notebooks>.h2,.sidebar .quarto-alternate-formats>h2,.sidebar .quarto-alternate-formats>.h2{font-size:.8rem}.sidebar nav[role=doc-toc]>h2,.sidebar nav[role=doc-toc]>.h2{font-size:.875rem}.sidebar nav[role=doc-toc]>ul a{border-left:1px solid #e9ecef;padding-left:.6rem}.sidebar .toc-actions h2>ul a,.sidebar .toc-actions .h2>ul a,.sidebar .quarto-code-links h2>ul a,.sidebar .quarto-code-links .h2>ul a,.sidebar .quarto-other-links h2>ul a,.sidebar .quarto-other-links .h2>ul a,.sidebar .quarto-alternate-notebooks h2>ul a,.sidebar .quarto-alternate-notebooks .h2>ul a,.sidebar .quarto-alternate-formats h2>ul a,.sidebar .quarto-alternate-formats .h2>ul a{border-left:none;padding-left:.6rem}.sidebar .toc-actions ul a:empty,.sidebar .quarto-code-links ul a:empty,.sidebar .quarto-other-links ul a:empty,.sidebar .quarto-alternate-notebooks ul a:empty,.sidebar .quarto-alternate-formats ul a:empty,.sidebar nav[role=doc-toc]>ul a:empty{display:none}.sidebar .toc-actions ul,.sidebar .quarto-code-links ul,.sidebar .quarto-other-links ul,.sidebar .quarto-alternate-notebooks ul,.sidebar .quarto-alternate-formats ul{padding-left:0;list-style:none}.sidebar nav[role=doc-toc] ul{list-style:none;padding-left:0;list-style:none}.sidebar nav[role=doc-toc]>ul{margin-left:.45em}.quarto-margin-sidebar nav[role=doc-toc]{padding-left:.5em}.sidebar .toc-actions>ul,.sidebar .quarto-code-links>ul,.sidebar .quarto-other-links>ul,.sidebar .quarto-alternate-notebooks>ul,.sidebar .quarto-alternate-formats>ul{font-size:.8rem}.sidebar nav[role=doc-toc]>ul{font-size:.875rem}.sidebar .toc-actions ul li a,.sidebar .quarto-code-links ul li a,.sidebar .quarto-other-links ul li a,.sidebar .quarto-alternate-notebooks ul li a,.sidebar .quarto-alternate-formats ul li a,.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>a.active,.sidebar nav[role=doc-toc] ul>li>ul>li>a.active{border-left:1px solid #2761e3;color:#2761e3 !important}.sidebar nav[role=doc-toc] ul>li>a:hover,.sidebar nav[role=doc-toc] ul>li>ul>li>a:hover{color:#2761e3 !important}kbd,.kbd{color:#343a40;background-color:#f8f9fa;border:1px solid;border-radius:5px;border-color:#dee2e6}.quarto-appendix-contents div.hanging-indent{margin-left:0em}.quarto-appendix-contents div.hanging-indent div.csl-entry{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}.callout{margin-top:1.25rem;margin-bottom:1.25rem;border-radius:.25rem;overflow-wrap:break-word}.callout .callout-title-container{overflow-wrap:anywhere}.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:not(.no-icon).callout-titled.callout-style-simple .callout-body{padding-left:1.6em}.callout.callout-titled>.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 .callout-body{padding-left:.5em;padding-right:.5em}.callout.callout-style-default .callout-body>:first-child{padding-top:.5rem;margin-top:0}.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-titled .callout-body>:last-child:not(.sourceCode),.callout.callout-titled .callout-body>div>:last-child:not(.sourceCode){padding-bottom:.5rem;margin-bottom:0}.callout:not(.callout-titled) .callout-body>:first-child,.callout:not(.callout-titled) .callout-body>div>:first-child{margin-top:.25rem}.callout:not(.callout-titled) .callout-body>:last-child,.callout:not(.callout-titled) .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-title-container{margin-top:-1px}.callout.callout-style-default.callout-caution:not(.no-icon) div.callout-icon-container{padding-top:.3em;padding-right:.35em}.callout>.callout-body>.callout-icon-container>.no-icon,.callout>.callout-header>.callout-icon-container>.no-icon{display:none}div.callout.callout{border-left-color:#6c757d}div.callout.callout-style-default>.callout-header{background-color:#6c757d}div.callout-note.callout{border-left-color:#2780e3}div.callout-note.callout-style-default>.callout-header{background-color:#e9f2fc}div.callout-note:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-tip.callout{border-left-color:#3fb618}div.callout-tip.callout-style-default>.callout-header{background-color:#ecf8e8}div.callout-tip:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-warning.callout{border-left-color:#ff7518}div.callout-warning.callout-style-default>.callout-header{background-color:#fff1e8}div.callout-warning:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-caution.callout{border-left-color:#f0ad4e}div.callout-caution.callout-style-default>.callout-header{background-color:#fef7ed}div.callout-caution:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-important.callout{border-left-color:#ff0039}div.callout-important.callout-style-default>.callout-header{background-color:#ffe6eb}div.callout-important:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-important.callout-titled .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}.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}.sidebar-navigation{padding-left:20px}.navbar{background-color:#2780e3;color:#fdfeff}.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:#343a40}.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}@media(max-width: 767.98px){.sidebar-menu-container{padding-bottom:5em}}.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 .footnotes ol{margin-left:.5em}#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{--bs-btn-color: #cacccd;--bs-btn-bg: #343a40;--bs-btn-border-color: #343a40;--bs-btn-hover-color: #cacccd;--bs-btn-hover-bg: #52585d;--bs-btn-hover-border-color: #484e53;--bs-btn-focus-shadow-rgb: 75, 80, 85;--bs-btn-active-color: #fff;--bs-btn-active-bg: #5d6166;--bs-btn-active-border-color: #484e53;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #343a40;--bs-btn-disabled-border-color: #343a40}nav.quarto-secondary-nav.color-navbar{background-color:#2780e3;color:#fdfeff}nav.quarto-secondary-nav.color-navbar h1,nav.quarto-secondary-nav.color-navbar .h1,nav.quarto-secondary-nav.color-navbar .quarto-btn-toggle{color:#fdfeff}@media(max-width: 991.98px){body.nav-sidebar .quarto-title-banner{margin-bottom:0;padding-bottom:1em}body.nav-sidebar #title-block-header{margin-block-end:0}}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)}.code-annotated .code-copy-button{margin-right:1.25em;margin-top:0;padding-bottom:0;padding-top:3px}.code-annotation-gutter-bg{background-color:#fff}.code-annotation-gutter{background-color:rgba(233,236,239,.65)}.code-annotation-gutter,.code-annotation-gutter-bg{height:100%;width:calc(20px + .5em);position:absolute;top:0;right:0}dl.code-annotation-container-grid dt{margin-right:1em;margin-top:.25rem}dl.code-annotation-container-grid dt{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;color:#4b545c;border:solid #4b545c 1px;border-radius:50%;height:22px;width:22px;line-height:22px;font-size:11px;text-align:center;vertical-align:middle;text-decoration:none}dl.code-annotation-container-grid dt[data-target-cell]{cursor:pointer}dl.code-annotation-container-grid dt[data-target-cell].code-annotation-active{color:#fff;border:solid #aaa 1px;background-color:#aaa}pre.code-annotation-code{padding-top:0;padding-bottom:0}pre.code-annotation-code code{z-index:3}#code-annotation-line-highlight-gutter{width:100%;border-top:solid rgba(170,170,170,.2666666667) 1px;border-bottom:solid rgba(170,170,170,.2666666667) 1px;z-index:2;background-color:rgba(170,170,170,.1333333333)}#code-annotation-line-highlight{margin-left:-4em;width:calc(100% + 4em);border-top:solid rgba(170,170,170,.2666666667) 1px;border-bottom:solid rgba(170,170,170,.2666666667) 1px;z-index:2;background-color:rgba(170,170,170,.1333333333)}code.sourceCode .code-annotation-anchor.code-annotation-active{background-color:var(--quarto-hl-normal-color, #aaaaaa);border:solid var(--quarto-hl-normal-color, #aaaaaa) 1px;color:#e9ecef;font-weight:bolder}code.sourceCode .code-annotation-anchor{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;color:var(--quarto-hl-co-color);border:solid var(--quarto-hl-co-color) 1px;border-radius:50%;height:18px;width:18px;font-size:9px;margin-top:2px}code.sourceCode button.code-annotation-anchor{padding:2px;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none}code.sourceCode a.code-annotation-anchor{line-height:18px;text-align:center;vertical-align:middle;cursor:default;text-decoration:none}@media print{.page-columns .column-screen-inset{grid-column:page-start-inset/page-end-inset;z-index:998;opacity:.999}.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;opacity:.999}.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;opacity:.999}.page-columns .column-screen-inset-right table{background:#fff}.page-columns .column-screen{grid-column:page-start/page-end;z-index:998;opacity:.999}.page-columns .column-screen table{background:#fff}.page-columns .column-screen-left{grid-column:page-start/body-content-end;z-index:998;opacity:.999}.page-columns .column-screen-left table{background:#fff}.page-columns .column-screen-right{grid-column:body-content-start/page-end;z-index:998;opacity:.999}.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:#f8f9fa;z-index:998;opacity:.999;margin-bottom:1em}}.quarto-video{margin-bottom:1em}.table{border-top:1px solid #ebedee;border-bottom:1px solid #ebedee}.table>thead{border-top-width:0;border-bottom:1px solid #b2bac1}.table a{word-break:break-word}.table>:not(caption)>*>*{background-color:unset;color:unset}#quarto-document-content .crosstalk-input .checkbox input[type=checkbox],#quarto-document-content .crosstalk-input .checkbox-inline input[type=checkbox]{position:unset;margin-top:unset;margin-left:unset}#quarto-document-content .row{margin-left:unset;margin-right:unset}.quarto-xref{white-space:nowrap}a.external:after{content:"";background-image:url('data:image/svg+xml,');background-size:contain;background-repeat:no-repeat;background-position:center center;margin-left:.2em;padding-right:.75em}div.sourceCode code a.external:after{content:none}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{background-color:rgba(219,219,219,.8)}.quarto-dark .code-with-filename .code-with-filename-file{background-color:#555}.code-with-filename .code-with-filename-file strong{font-weight:400}.quarto-title-banner{margin-bottom:1em;color:#fdfeff;background:#2780e3}.quarto-title-banner a{color:#fdfeff}.quarto-title-banner h1,.quarto-title-banner .h1,.quarto-title-banner h2,.quarto-title-banner .h2{color:#fdfeff}.quarto-title-banner .code-tools-button{color:#97cbff}.quarto-title-banner .code-tools-button:hover{color:#fdfeff}.quarto-title-banner .code-tools-button>.bi::before{background-image:url('data:image/svg+xml,')}.quarto-title-banner .code-tools-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}.quarto-title-banner .quarto-title .title{font-weight:600}.quarto-title-banner .quarto-categories{margin-top:.75em}@media(min-width: 992px){.quarto-title-banner{padding-top:2.5em;padding-bottom:2.5em}}@media(max-width: 991.98px){.quarto-title-banner{padding-top:1em;padding-bottom:1em}}@media(max-width: 767.98px){body.hypothesis-enabled #title-block-header>*{padding-right:20px}}main.quarto-banner-title-block>section:first-child>h2,main.quarto-banner-title-block>section:first-child>.h2,main.quarto-banner-title-block>section:first-child>h3,main.quarto-banner-title-block>section:first-child>.h3,main.quarto-banner-title-block>section:first-child>h4,main.quarto-banner-title-block>section:first-child>.h4{margin-top:0}.quarto-title .quarto-categories{display:flex;flex-wrap:wrap;row-gap:.5em;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}.quarto-title-meta-container{display:grid;grid-template-columns:1fr auto}.quarto-title-meta-column-end{display:flex;flex-direction:column;padding-left:1em}.quarto-title-meta-column-end a .bi{margin-right:.3em}#title-block-header.quarto-title-block.default .quarto-title-meta{display:grid;grid-template-columns:minmax(max-content, 1fr) 1fr;grid-column-gap:1em}#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:-0.2em;height:.8em;width:.8em}#title-block-header.quarto-title-block.default .quarto-title-author-email{opacity:.7}#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 p.affiliation:last-of-type{margin-bottom:.1em}#title-block-header.quarto-title-block.default p.affiliation{margin-bottom:.1em}#title-block-header.quarto-title-block.default .keywords,#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 .keywords>p,#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 .keywords>p:last-of-type,#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 .keywords .block-title,#title-block-header.quarto-title-block.default .description .block-title,#title-block-header.quarto-title-block.default .abstract .block-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:minmax(max-content, 1fr) 1fr;grid-column-gap:1em}.quarto-title-tools-only{display:flex;justify-content:right}body{-webkit-font-smoothing:antialiased}.badge.bg-light{color:#343a40}.progress .progress-bar{font-size:8px;line-height:8px} diff --git a/_site/site_libs/bootstrap/bootstrap.min.js b/_site/site_libs/bootstrap/bootstrap.min.js new file mode 100644 index 0000000..e8f21f7 --- /dev/null +++ b/_site/site_libs/bootstrap/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v5.3.1 (https://getbootstrap.com/) + * Copyright 2011-2023 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=new Map,e={set(e,i,n){t.has(e)||t.set(e,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i="transitionend",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),s=t=>{t.dispatchEvent(new Event(i))},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(n(t)):null,a=t=>{if(!o(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},l=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),c=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?c(t.parentNode):null},h=()=>{},d=t=>{t.offsetHeight},u=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,f=[],p=()=>"rtl"===document.documentElement.dir,m=t=>{var e;e=()=>{const e=u();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?(f.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of f)t()})),f.push(e)):e()},g=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,_=(t,e,n=!0)=>{if(!n)return void g(t);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})(e)+5;let r=!1;const a=({target:n})=>{n===e&&(r=!0,e.removeEventListener(i,a),g(t))};e.addEventListener(i,a),setTimeout((()=>{r||s(e)}),o)},b=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},v=/[^.]*(?=\..*)\.|.*/,y=/\..*/,w=/::\d+$/,A={};let E=1;const T={mouseenter:"mouseover",mouseleave:"mouseout"},C=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 O(t,e){return e&&`${e}::${E++}`||t.uidEvent||E++}function x(t){const e=O(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function k(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function L(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=I(t);return C.has(o)||(o=t),[n,s,o]}function S(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=L(e,i,n);if(e in T){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=x(t),c=l[a]||(l[a]={}),h=k(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=O(r,e.replace(v,"")),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(const a of o)if(a===r)return P(s,{delegateTarget:r}),n.oneOff&&N.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return P(n,{delegateTarget:t}),i.oneOff&&N.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function D(t,e,i,n,s){const o=k(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function $(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&D(t,e,i,r.callable,r.delegationSelector)}function I(t){return t=t.replace(y,""),T[t]||t}const N={on(t,e,i,n){S(t,e,i,n,!1)},one(t,e,i,n){S(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=L(e,i,n),a=r!==e,l=x(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))$(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(w,"");a&&!e.includes(s)||D(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;D(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=u();let s=null,o=!0,r=!0,a=!1;e!==I(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=P(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function P(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function M(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function j(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const F={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${j(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${j(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=M(t.dataset[n])}return e},getDataAttribute:(t,e)=>M(t.getAttribute(`data-bs-${j(e)}`))};class H{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=o(e)?F.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...o(e)?F.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],r=o(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(r))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${r}" but expected type "${s}".`)}var i}}class W extends H{constructor(t,i){super(),(t=r(t))&&(this._element=t,this._config=this._getConfig(i),e.set(this._element,this.constructor.DATA_KEY,this))}dispose(){e.remove(this._element,this.constructor.DATA_KEY),N.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){_(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return e.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.3.1"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const B=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 n(e)},z={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.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);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=>!l(t)&&a(t)))},getSelectorFromElement(t){const e=B(t);return e&&z.findOne(e)?e:null},getElementFromSelector(t){const e=B(t);return e?z.findOne(e):null},getMultipleElementsFromSelector(t){const e=B(t);return e?z.find(e):[]}},R=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;N.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),l(this))return;const s=z.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))},q=".bs.alert",V=`close${q}`,K=`closed${q}`;class Q extends W{static get NAME(){return"alert"}close(){if(N.trigger(this._element,V).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(),N.trigger(this._element,K),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Q.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(Q,"close"),m(Q);const X='[data-bs-toggle="button"]';class Y extends W{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=Y.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}N.on(document,"click.bs.button.data-api",X,(t=>{t.preventDefault();const e=t.target.closest(X);Y.getOrCreateInstance(e).toggle()})),m(Y);const U=".bs.swipe",G=`touchstart${U}`,J=`touchmove${U}`,Z=`touchend${U}`,tt=`pointerdown${U}`,et=`pointerup${U}`,it={endCallback:null,leftCallback:null,rightCallback:null},nt={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class st extends H{constructor(t,e){super(),this._element=t,t&&st.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return it}static get DefaultType(){return nt}static get NAME(){return"swipe"}dispose(){N.off(this._element,U)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),g(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&g(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(N.on(this._element,tt,(t=>this._start(t))),N.on(this._element,et,(t=>this._end(t))),this._element.classList.add("pointer-event")):(N.on(this._element,G,(t=>this._start(t))),N.on(this._element,J,(t=>this._move(t))),N.on(this._element,Z,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const ot=".bs.carousel",rt=".data-api",at="next",lt="prev",ct="left",ht="right",dt=`slide${ot}`,ut=`slid${ot}`,ft=`keydown${ot}`,pt=`mouseenter${ot}`,mt=`mouseleave${ot}`,gt=`dragstart${ot}`,_t=`load${ot}${rt}`,bt=`click${ot}${rt}`,vt="carousel",yt="active",wt=".active",At=".carousel-item",Et=wt+At,Tt={ArrowLeft:ht,ArrowRight:ct},Ct={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},Ot={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class xt extends W{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=z.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===vt&&this.cycle()}static get Default(){return Ct}static get DefaultType(){return Ot}static get NAME(){return"carousel"}next(){this._slide(at)}nextWhenVisible(){!document.hidden&&a(this._element)&&this.next()}prev(){this._slide(lt)}pause(){this._isSliding&&s(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?N.one(this._element,ut,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void N.one(this._element,ut,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?at:lt;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&N.on(this._element,ft,(t=>this._keydown(t))),"hover"===this._config.pause&&(N.on(this._element,pt,(()=>this.pause())),N.on(this._element,mt,(()=>this._maybeEnableCycle()))),this._config.touch&&st.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of z.find(".carousel-item img",this._element))N.on(t,gt,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(ct)),rightCallback:()=>this._slide(this._directionToOrder(ht)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new st(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=Tt[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=z.findOne(wt,this._indicatorsElement);e.classList.remove(yt),e.removeAttribute("aria-current");const i=z.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(yt),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===at,s=e||b(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>N.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(dt).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),d(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(yt),i.classList.remove(yt,c,l),this._isSliding=!1,r(ut)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return z.findOne(Et,this._element)}_getItems(){return z.find(At,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return p()?t===ct?lt:at:t===ct?at:lt}_orderToDirection(t){return p()?t===lt?ct:ht:t===lt?ht:ct}static jQueryInterface(t){return this.each((function(){const e=xt.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}N.on(document,bt,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=z.getElementFromSelector(this);if(!e||!e.classList.contains(vt))return;t.preventDefault();const i=xt.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===F.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),N.on(window,_t,(()=>{const t=z.find('[data-bs-ride="carousel"]');for(const e of t)xt.getOrCreateInstance(e)})),m(xt);const kt=".bs.collapse",Lt=`show${kt}`,St=`shown${kt}`,Dt=`hide${kt}`,$t=`hidden${kt}`,It=`click${kt}.data-api`,Nt="show",Pt="collapse",Mt="collapsing",jt=`:scope .${Pt} .${Pt}`,Ft='[data-bs-toggle="collapse"]',Ht={parent:null,toggle:!0},Wt={parent:"(null|element)",toggle:"boolean"};class Bt extends W{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=z.find(Ft);for(const t of i){const e=z.getSelectorFromElement(t),i=z.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return Ht}static get DefaultType(){return Wt}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>Bt.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(N.trigger(this._element,Lt).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(Pt),this._element.classList.add(Mt),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(Mt),this._element.classList.add(Pt,Nt),this._element.style[e]="",N.trigger(this._element,St)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(N.trigger(this._element,Dt).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,d(this._element),this._element.classList.add(Mt),this._element.classList.remove(Pt,Nt);for(const t of this._triggerArray){const e=z.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(Mt),this._element.classList.add(Pt),N.trigger(this._element,$t)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(Nt)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=r(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(Ft);for(const e of t){const t=z.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=z.find(jt,this._config.parent);return z.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=Bt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}N.on(document,It,Ft,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of z.getMultipleElementsFromSelector(this))Bt.getOrCreateInstance(t,{toggle:!1}).toggle()})),m(Bt);var zt="top",Rt="bottom",qt="right",Vt="left",Kt="auto",Qt=[zt,Rt,qt,Vt],Xt="start",Yt="end",Ut="clippingParents",Gt="viewport",Jt="popper",Zt="reference",te=Qt.reduce((function(t,e){return t.concat([e+"-"+Xt,e+"-"+Yt])}),[]),ee=[].concat(Qt,[Kt]).reduce((function(t,e){return t.concat([e,e+"-"+Xt,e+"-"+Yt])}),[]),ie="beforeRead",ne="read",se="afterRead",oe="beforeMain",re="main",ae="afterMain",le="beforeWrite",ce="write",he="afterWrite",de=[ie,ne,se,oe,re,ae,le,ce,he];function ue(t){return t?(t.nodeName||"").toLowerCase():null}function fe(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function pe(t){return t instanceof fe(t).Element||t instanceof Element}function me(t){return t instanceof fe(t).HTMLElement||t instanceof HTMLElement}function ge(t){return"undefined"!=typeof ShadowRoot&&(t instanceof fe(t).ShadowRoot||t instanceof ShadowRoot)}const _e={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];me(s)&&ue(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}),{});me(n)&&ue(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function be(t){return t.split("-")[0]}var ve=Math.max,ye=Math.min,we=Math.round;function Ae(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function Ee(){return!/^((?!chrome|android).)*safari/i.test(Ae())}function Te(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&me(t)&&(s=t.offsetWidth>0&&we(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&we(n.height)/t.offsetHeight||1);var r=(pe(t)?fe(t):window).visualViewport,a=!Ee()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function Ce(t){var e=Te(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 Oe(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&ge(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function xe(t){return fe(t).getComputedStyle(t)}function ke(t){return["table","td","th"].indexOf(ue(t))>=0}function Le(t){return((pe(t)?t.ownerDocument:t.document)||window.document).documentElement}function Se(t){return"html"===ue(t)?t:t.assignedSlot||t.parentNode||(ge(t)?t.host:null)||Le(t)}function De(t){return me(t)&&"fixed"!==xe(t).position?t.offsetParent:null}function $e(t){for(var e=fe(t),i=De(t);i&&ke(i)&&"static"===xe(i).position;)i=De(i);return i&&("html"===ue(i)||"body"===ue(i)&&"static"===xe(i).position)?e:i||function(t){var e=/firefox/i.test(Ae());if(/Trident/i.test(Ae())&&me(t)&&"fixed"===xe(t).position)return null;var i=Se(t);for(ge(i)&&(i=i.host);me(i)&&["html","body"].indexOf(ue(i))<0;){var n=xe(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 Ie(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function Ne(t,e,i){return ve(t,ye(e,i))}function Pe(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function Me(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const je={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=be(i.placement),l=Ie(a),c=[Vt,qt].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return Pe("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:Me(t,Qt))}(s.padding,i),d=Ce(o),u="y"===l?zt:Vt,f="y"===l?Rt:qt,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=$e(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,A=Ne(v,w,y),E=l;i.modifiersData[n]=((e={})[E]=A,e.centerOffset=A-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)))&&Oe(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Fe(t){return t.split("-")[1]}var He={top:"auto",right:"auto",bottom:"auto",left:"auto"};function We(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=t.isFixed,u=r.x,f=void 0===u?0:u,p=r.y,m=void 0===p?0:p,g="function"==typeof h?h({x:f,y:m}):{x:f,y:m};f=g.x,m=g.y;var _=r.hasOwnProperty("x"),b=r.hasOwnProperty("y"),v=Vt,y=zt,w=window;if(c){var A=$e(i),E="clientHeight",T="clientWidth";A===fe(i)&&"static"!==xe(A=Le(i)).position&&"absolute"===a&&(E="scrollHeight",T="scrollWidth"),(s===zt||(s===Vt||s===qt)&&o===Yt)&&(y=Rt,m-=(d&&A===w&&w.visualViewport?w.visualViewport.height:A[E])-n.height,m*=l?1:-1),s!==Vt&&(s!==zt&&s!==Rt||o!==Yt)||(v=qt,f-=(d&&A===w&&w.visualViewport?w.visualViewport.width:A[T])-n.width,f*=l?1:-1)}var C,O=Object.assign({position:a},c&&He),x=!0===h?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:we(i*s)/s||0,y:we(n*s)/s||0}}({x:f,y:m},fe(i)):{x:f,y:m};return f=x.x,m=x.y,l?Object.assign({},O,((C={})[y]=b?"0":"",C[v]=_?"0":"",C.transform=(w.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",C)):Object.assign({},O,((e={})[y]=b?m+"px":"",e[v]=_?f+"px":"",e.transform="",e))}const Be={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:be(e.placement),variation:Fe(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,We(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,We(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 ze={passive:!0};const Re={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=fe(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,ze)})),a&&l.addEventListener("resize",i.update,ze),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,ze)})),a&&l.removeEventListener("resize",i.update,ze)}},data:{}};var qe={left:"right",right:"left",bottom:"top",top:"bottom"};function Ve(t){return t.replace(/left|right|bottom|top/g,(function(t){return qe[t]}))}var Ke={start:"end",end:"start"};function Qe(t){return t.replace(/start|end/g,(function(t){return Ke[t]}))}function Xe(t){var e=fe(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function Ye(t){return Te(Le(t)).left+Xe(t).scrollLeft}function Ue(t){var e=xe(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ge(t){return["html","body","#document"].indexOf(ue(t))>=0?t.ownerDocument.body:me(t)&&Ue(t)?t:Ge(Se(t))}function Je(t,e){var i;void 0===e&&(e=[]);var n=Ge(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=fe(n),r=s?[o].concat(o.visualViewport||[],Ue(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(Je(Se(r)))}function Ze(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function ti(t,e,i){return e===Gt?Ze(function(t,e){var i=fe(t),n=Le(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=Ee();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+Ye(t),y:l}}(t,i)):pe(e)?function(t,e){var i=Te(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):Ze(function(t){var e,i=Le(t),n=Xe(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=ve(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=ve(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+Ye(t),l=-n.scrollTop;return"rtl"===xe(s||i).direction&&(a+=ve(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(Le(t)))}function ei(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?be(s):null,r=s?Fe(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case zt:e={x:a,y:i.y-n.height};break;case Rt:e={x:a,y:i.y+i.height};break;case qt:e={x:i.x+i.width,y:l};break;case Vt:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?Ie(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case Xt:e[c]=e[c]-(i[h]/2-n[h]/2);break;case Yt:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function ii(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.strategy,r=void 0===o?t.strategy:o,a=i.boundary,l=void 0===a?Ut:a,c=i.rootBoundary,h=void 0===c?Gt:c,d=i.elementContext,u=void 0===d?Jt:d,f=i.altBoundary,p=void 0!==f&&f,m=i.padding,g=void 0===m?0:m,_=Pe("number"!=typeof g?g:Me(g,Qt)),b=u===Jt?Zt:Jt,v=t.rects.popper,y=t.elements[p?b:u],w=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=Je(Se(t)),i=["absolute","fixed"].indexOf(xe(t).position)>=0&&me(t)?$e(t):t;return pe(i)?e.filter((function(t){return pe(t)&&Oe(t,i)&&"body"!==ue(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=ti(t,i,n);return e.top=ve(s.top,e.top),e.right=ye(s.right,e.right),e.bottom=ye(s.bottom,e.bottom),e.left=ve(s.left,e.left),e}),ti(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(pe(y)?y:y.contextElement||Le(t.elements.popper),l,h,r),A=Te(t.elements.reference),E=ei({reference:A,element:v,strategy:"absolute",placement:s}),T=Ze(Object.assign({},v,E)),C=u===Jt?T:A,O={top:w.top-C.top+_.top,bottom:C.bottom-w.bottom+_.bottom,left:w.left-C.left+_.left,right:C.right-w.right+_.right},x=t.modifiersData.offset;if(u===Jt&&x){var k=x[s];Object.keys(O).forEach((function(t){var e=[qt,Rt].indexOf(t)>=0?1:-1,i=[zt,Rt].indexOf(t)>=0?"y":"x";O[t]+=k[i]*e}))}return O}function ni(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?ee:l,h=Fe(n),d=h?a?te:te.filter((function(t){return Fe(t)===h})):Qt,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]=ii(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[be(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const si={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,_=be(g),b=l||(_!==g&&p?function(t){if(be(t)===Kt)return[];var e=Ve(t);return[Qe(t),e,Qe(e)]}(g):[Ve(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(be(i)===Kt?ni(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,A=new Map,E=!0,T=v[0],C=0;C=0,S=L?"width":"height",D=ii(e,{placement:O,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),$=L?k?qt:Vt:k?Rt:zt;y[S]>w[S]&&($=Ve($));var I=Ve($),N=[];if(o&&N.push(D[x]<=0),a&&N.push(D[$]<=0,D[I]<=0),N.every((function(t){return t}))){T=O,E=!1;break}A.set(O,N)}if(E)for(var P=function(t){var e=v.find((function(e){var i=A.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"!==P(M);M--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function oi(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 ri(t){return[zt,qt,Rt,Vt].some((function(e){return t[e]>=0}))}const ai={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=ii(e,{elementContext:"reference"}),a=ii(e,{altBoundary:!0}),l=oi(r,n),c=oi(a,s,o),h=ri(l),d=ri(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})}},li={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=ee.reduce((function(t,i){return t[i]=function(t,e,i){var n=be(t),s=[Vt,zt].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,[Vt,qt].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}},ci={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=ei({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},hi={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=ii(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=be(e.placement),b=Fe(e.placement),v=!b,y=Ie(_),w="x"===y?"y":"x",A=e.modifiersData.popperOffsets,E=e.rects.reference,T=e.rects.popper,C="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,O="number"==typeof C?{mainAxis:C,altAxis:C}:Object.assign({mainAxis:0,altAxis:0},C),x=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,k={x:0,y:0};if(A){if(o){var L,S="y"===y?zt:Vt,D="y"===y?Rt:qt,$="y"===y?"height":"width",I=A[y],N=I+g[S],P=I-g[D],M=f?-T[$]/2:0,j=b===Xt?E[$]:T[$],F=b===Xt?-T[$]:-E[$],H=e.elements.arrow,W=f&&H?Ce(H):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},z=B[S],R=B[D],q=Ne(0,E[$],W[$]),V=v?E[$]/2-M-q-z-O.mainAxis:j-q-z-O.mainAxis,K=v?-E[$]/2+M+q+R+O.mainAxis:F+q+R+O.mainAxis,Q=e.elements.arrow&&$e(e.elements.arrow),X=Q?"y"===y?Q.clientTop||0:Q.clientLeft||0:0,Y=null!=(L=null==x?void 0:x[y])?L:0,U=I+K-Y,G=Ne(f?ye(N,I+V-Y-X):N,I,f?ve(P,U):P);A[y]=G,k[y]=G-I}if(a){var J,Z="x"===y?zt:Vt,tt="x"===y?Rt:qt,et=A[w],it="y"===w?"height":"width",nt=et+g[Z],st=et-g[tt],ot=-1!==[zt,Vt].indexOf(_),rt=null!=(J=null==x?void 0:x[w])?J:0,at=ot?nt:et-E[it]-T[it]-rt+O.altAxis,lt=ot?et+E[it]+T[it]-rt-O.altAxis:st,ct=f&&ot?function(t,e,i){var n=Ne(t,e,i);return n>i?i:n}(at,et,lt):Ne(f?at:nt,et,f?lt:st);A[w]=ct,k[w]=ct-et}e.modifiersData[n]=k}},requiresIfExists:["offset"]};function di(t,e,i){void 0===i&&(i=!1);var n,s,o=me(e),r=me(e)&&function(t){var e=t.getBoundingClientRect(),i=we(e.width)/t.offsetWidth||1,n=we(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=Le(e),l=Te(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==ue(e)||Ue(a))&&(c=(n=e)!==fe(n)&&me(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:Xe(n)),me(e)?((h=Te(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=Ye(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function ui(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 fi={placement:"bottom",modifiers:[],strategy:"absolute"};function pi(){for(var t=arguments.length,e=new Array(t),i=0;iNumber.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(this._inNavbar||"static"===this._config.display)&&(F.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...g(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=z.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>a(t)));i.length&&b(i,e,t===Ti,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=qi.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(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=z.find(Ni);for(const i of e){const e=qi.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Ei,Ti].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Ii)?this:z.prev(this,Ii)[0]||z.next(this,Ii)[0]||z.findOne(Ii,t.delegateTarget.parentNode),o=qi.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}N.on(document,Si,Ii,qi.dataApiKeydownHandler),N.on(document,Si,Pi,qi.dataApiKeydownHandler),N.on(document,Li,qi.clearMenus),N.on(document,Di,qi.clearMenus),N.on(document,Li,Ii,(function(t){t.preventDefault(),qi.getOrCreateInstance(this).toggle()})),m(qi);const Vi="backdrop",Ki="show",Qi=`mousedown.bs.${Vi}`,Xi={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Yi={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Ui extends H{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Xi}static get DefaultType(){return Yi}static get NAME(){return Vi}show(t){if(!this._config.isVisible)return void g(t);this._append();const e=this._getElement();this._config.isAnimated&&d(e),e.classList.add(Ki),this._emulateAnimation((()=>{g(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Ki),this._emulateAnimation((()=>{this.dispose(),g(t)}))):g(t)}dispose(){this._isAppended&&(N.off(this._element,Qi),this._element.remove(),this._isAppended=!1)}_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}_configAfterMerge(t){return t.rootElement=r(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),N.on(t,Qi,(()=>{g(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){_(t,this._getElement(),this._config.isAnimated)}}const Gi=".bs.focustrap",Ji=`focusin${Gi}`,Zi=`keydown.tab${Gi}`,tn="backward",en={autofocus:!0,trapElement:null},nn={autofocus:"boolean",trapElement:"element"};class sn extends H{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return en}static get DefaultType(){return nn}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),N.off(document,Gi),N.on(document,Ji,(t=>this._handleFocusin(t))),N.on(document,Zi,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,N.off(document,Gi))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=z.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===tn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?tn:"forward")}}const on=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",rn=".sticky-top",an="padding-right",ln="margin-right";class cn{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,an,(e=>e+t)),this._setElementAttributes(on,an,(e=>e+t)),this._setElementAttributes(rn,ln,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,an),this._resetElementAttributes(on,an),this._resetElementAttributes(rn,ln)}isOverflowing(){return this.getWidth()>0}_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).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&F.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=F.getDataAttribute(t,e);null!==i?(F.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(o(t))e(t);else for(const i of z.find(t,this._element))e(i)}}const hn=".bs.modal",dn=`hide${hn}`,un=`hidePrevented${hn}`,fn=`hidden${hn}`,pn=`show${hn}`,mn=`shown${hn}`,gn=`resize${hn}`,_n=`click.dismiss${hn}`,bn=`mousedown.dismiss${hn}`,vn=`keydown.dismiss${hn}`,yn=`click${hn}.data-api`,wn="modal-open",An="show",En="modal-static",Tn={backdrop:!0,focus:!0,keyboard:!0},Cn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class On extends W{constructor(t,e){super(t,e),this._dialog=z.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new cn,this._addEventListeners()}static get Default(){return Tn}static get DefaultType(){return Cn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||N.trigger(this._element,pn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(wn),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(N.trigger(this._element,dn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(An),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){N.off(window,hn),N.off(this._dialog,hn),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Ui({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||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;const e=z.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),d(this._element),this._element.classList.add(An),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,N.trigger(this._element,mn,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){N.on(this._element,vn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),N.on(window,gn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),N.on(this._element,bn,(t=>{N.one(this._element,_n,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_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(wn),this._resetAdjustments(),this._scrollBar.reset(),N.trigger(this._element,fn)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(N.trigger(this._element,un).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(En)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(En),this._queueCallback((()=>{this._element.classList.remove(En),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=p()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=p()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=On.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}N.on(document,yn,'[data-bs-toggle="modal"]',(function(t){const e=z.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),N.one(e,pn,(t=>{t.defaultPrevented||N.one(e,fn,(()=>{a(this)&&this.focus()}))}));const i=z.findOne(".modal.show");i&&On.getInstance(i).hide(),On.getOrCreateInstance(e).toggle(this)})),R(On),m(On);const xn=".bs.offcanvas",kn=".data-api",Ln=`load${xn}${kn}`,Sn="show",Dn="showing",$n="hiding",In=".offcanvas.show",Nn=`show${xn}`,Pn=`shown${xn}`,Mn=`hide${xn}`,jn=`hidePrevented${xn}`,Fn=`hidden${xn}`,Hn=`resize${xn}`,Wn=`click${xn}${kn}`,Bn=`keydown.dismiss${xn}`,zn={backdrop:!0,keyboard:!0,scroll:!1},Rn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class qn extends W{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return zn}static get DefaultType(){return Rn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||N.trigger(this._element,Nn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new cn).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Dn),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(Sn),this._element.classList.remove(Dn),N.trigger(this._element,Pn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(N.trigger(this._element,Mn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add($n),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(Sn,$n),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new cn).reset(),N.trigger(this._element,Fn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Ui({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():N.trigger(this._element,jn)}:null})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_addEventListeners(){N.on(this._element,Bn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():N.trigger(this._element,jn))}))}static jQueryInterface(t){return this.each((function(){const e=qn.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)}}))}}N.on(document,Wn,'[data-bs-toggle="offcanvas"]',(function(t){const e=z.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this))return;N.one(e,Fn,(()=>{a(this)&&this.focus()}));const i=z.findOne(In);i&&i!==e&&qn.getInstance(i).hide(),qn.getOrCreateInstance(e).toggle(this)})),N.on(window,Ln,(()=>{for(const t of z.find(In))qn.getOrCreateInstance(t).show()})),N.on(window,Hn,(()=>{for(const t of z.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&qn.getOrCreateInstance(t).hide()})),R(qn),m(qn);const Vn={"*":["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:[]},Kn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Qn=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Xn=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Kn.has(i)||Boolean(Qn.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Yn={allowList:Vn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Un={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Gn={entry:"(string|element|function|null)",selector:"(string|element)"};class Jn extends H{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Yn}static get DefaultType(){return Un}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},Gn)}_setContent(t,e,i){const n=z.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?o(e)?this._putElementInTemplate(r(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Xn(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return g(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const Zn=new Set(["sanitize","allowList","sanitizeFn"]),ts="fade",es="show",is=".modal",ns="hide.bs.modal",ss="hover",os="focus",rs={AUTO:"auto",TOP:"top",RIGHT:p()?"left":"right",BOTTOM:"bottom",LEFT:p()?"right":"left"},as={allowList:Vn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},ls={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class cs extends W{constructor(t,e){if(void 0===vi)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,e),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return as}static get DefaultType(){return ls}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),N.off(this._element.closest(is),ns,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),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=N.trigger(this._element,this.constructor.eventName("show")),e=(c(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),N.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.on(t,"mouseover",h);this._queueCallback((()=>{N.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!N.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.off(t,"mouseover",h);this._activeTrigger.click=!1,this._activeTrigger[os]=!1,this._activeTrigger[ss]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),N.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(ts,es),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(ts),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new Jn({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{".tooltip-inner":this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(ts)}_isShown(){return this.tip&&this.tip.classList.contains(es)}_createPopper(t){const e=g(this._config.placement,[this,t,this._element]),i=rs[e.toUpperCase()];return bi(this._element,t,this._getPopperConfig(i))}_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 g(t,[this._element])}_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:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...g(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)N.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===ss?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===ss?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");N.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?os:ss]=!0,e._enter()})),N.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?os:ss]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},N.on(this._element.closest(is),ns,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=F.getDataAttributes(this._element);for(const t of Object.keys(e))Zn.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return 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()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=cs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(cs);const hs={...cs.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},ds={...cs.DefaultType,content:"(null|string|element|function)"};class us extends cs{static get Default(){return hs}static get DefaultType(){return ds}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{".popover-header":this._getTitle(),".popover-body":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=us.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(us);const fs=".bs.scrollspy",ps=`activate${fs}`,ms=`click${fs}`,gs=`load${fs}.data-api`,_s="active",bs="[href]",vs=".nav-link",ys=`${vs}, .nav-item > ${vs}, .list-group-item`,ws={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},As={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class Es extends W{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return ws}static get DefaultType(){return As}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=r(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(N.off(this._config.target,ms),N.on(this._config.target,ms,bs,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=z.find(bs,this._config.target);for(const e of t){if(!e.hash||l(e))continue;const t=z.findOne(decodeURI(e.hash),this._element);a(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(_s),this._activateParents(t),N.trigger(this._element,ps,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))z.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(_s);else for(const e of z.parents(t,".nav, .list-group"))for(const t of z.prev(e,ys))t.classList.add(_s)}_clearActiveClass(t){t.classList.remove(_s);const e=z.find(`${bs}.${_s}`,t);for(const t of e)t.classList.remove(_s)}static jQueryInterface(t){return this.each((function(){const e=Es.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]()}}))}}N.on(window,gs,(()=>{for(const t of z.find('[data-bs-spy="scroll"]'))Es.getOrCreateInstance(t)})),m(Es);const Ts=".bs.tab",Cs=`hide${Ts}`,Os=`hidden${Ts}`,xs=`show${Ts}`,ks=`shown${Ts}`,Ls=`click${Ts}`,Ss=`keydown${Ts}`,Ds=`load${Ts}`,$s="ArrowLeft",Is="ArrowRight",Ns="ArrowUp",Ps="ArrowDown",Ms="Home",js="End",Fs="active",Hs="fade",Ws="show",Bs=":not(.dropdown-toggle)",zs='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',Rs=`.nav-link${Bs}, .list-group-item${Bs}, [role="tab"]${Bs}, ${zs}`,qs=`.${Fs}[data-bs-toggle="tab"], .${Fs}[data-bs-toggle="pill"], .${Fs}[data-bs-toggle="list"]`;class Vs extends W{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),N.on(this._element,Ss,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?N.trigger(e,Cs,{relatedTarget:t}):null;N.trigger(t,xs,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(Fs),this._activate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),N.trigger(t,ks,{relatedTarget:e})):t.classList.add(Ws)}),t,t.classList.contains(Hs)))}_deactivate(t,e){t&&(t.classList.remove(Fs),t.blur(),this._deactivate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),N.trigger(t,Os,{relatedTarget:e})):t.classList.remove(Ws)}),t,t.classList.contains(Hs)))}_keydown(t){if(![$s,Is,Ns,Ps,Ms,js].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!l(t)));let i;if([Ms,js].includes(t.key))i=e[t.key===Ms?0:e.length-1];else{const n=[Is,Ps].includes(t.key);i=b(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Vs.getOrCreateInstance(i).show())}_getChildren(){return z.find(Rs,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=z.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=z.findOne(t,i);s&&s.classList.toggle(n,e)};n(".dropdown-toggle",Fs),n(".dropdown-menu",Ws),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(Fs)}_getInnerElement(t){return t.matches(Rs)?t:z.findOne(Rs,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=Vs.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}N.on(document,Ls,zs,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this)||Vs.getOrCreateInstance(this).show()})),N.on(window,Ds,(()=>{for(const t of z.find(qs))Vs.getOrCreateInstance(t)})),m(Vs);const Ks=".bs.toast",Qs=`mouseover${Ks}`,Xs=`mouseout${Ks}`,Ys=`focusin${Ks}`,Us=`focusout${Ks}`,Gs=`hide${Ks}`,Js=`hidden${Ks}`,Zs=`show${Ks}`,to=`shown${Ks}`,eo="hide",io="show",no="showing",so={animation:"boolean",autohide:"boolean",delay:"number"},oo={animation:!0,autohide:!0,delay:5e3};class ro extends W{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return oo}static get DefaultType(){return so}static get NAME(){return"toast"}show(){N.trigger(this._element,Zs).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(eo),d(this._element),this._element.classList.add(io,no),this._queueCallback((()=>{this._element.classList.remove(no),N.trigger(this._element,to),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(N.trigger(this._element,Gs).defaultPrevented||(this._element.classList.add(no),this._queueCallback((()=>{this._element.classList.add(eo),this._element.classList.remove(no,io),N.trigger(this._element,Js)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(io),super.dispose()}isShown(){return this._element.classList.contains(io)}_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(){N.on(this._element,Qs,(t=>this._onInteraction(t,!0))),N.on(this._element,Xs,(t=>this._onInteraction(t,!1))),N.on(this._element,Ys,(t=>this._onInteraction(t,!0))),N.on(this._element,Us,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=ro.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return R(ro),m(ro),{Alert:Q,Button:Y,Carousel:xt,Collapse:Bt,Dropdown:qi,Modal:On,Offcanvas:qn,Popover:us,ScrollSpy:Es,Tab:Vs,Toast:ro,Tooltip:cs}})); +//# sourceMappingURL=bootstrap.bundle.min.js.map \ No newline at end of file diff --git a/_site/site_libs/clipboard/clipboard.min.js b/_site/site_libs/clipboard/clipboard.min.js new file mode 100644 index 0000000..1103f81 --- /dev/null +++ b/_site/site_libs/clipboard/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.11 + * 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 b}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),r=n.n(e);function c(t){try{return document.execCommand(t)}catch(t){return}}var a=function(t){t=r()(t);return c("cut"),t};function o(t,e){var n,o,t=(n=t,o="rtl"===document.documentElement.getAttribute("dir"),(t=document.createElement("textarea")).style.fontSize="12pt",t.style.border="0",t.style.padding="0",t.style.margin="0",t.style.position="absolute",t.style[o?"right":"left"]="-9999px",o=window.pageYOffset||document.documentElement.scrollTop,t.style.top="".concat(o,"px"),t.setAttribute("readonly",""),t.value=n,t);return e.container.appendChild(t),e=r()(t),c("copy"),t.remove(),e}var f=function(t){var e=1.anchorjs-link,.anchorjs-link:focus{opacity:1}",A.sheet.cssRules.length),A.sheet.insertRule("[data-anchorjs-icon]::after{content:attr(data-anchorjs-icon)}",A.sheet.cssRules.length),A.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")}',A.sheet.cssRules.length)),h=document.querySelectorAll("[id]"),t=[].map.call(h,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/site_libs/quarto-html/popper.min.js b/_site/site_libs/quarto-html/popper.min.js new file mode 100644 index 0000000..e3726d7 --- /dev/null +++ b/_site/site_libs/quarto-html/popper.min.js @@ -0,0 +1,6 @@ +/** + * @popperjs/core v2.11.7 - 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(){var e=navigator.userAgentData;return null!=e&&e.brands&&Array.isArray(e.brands)?e.brands.map((function(e){return e.brand+"/"+e.version})).join(" "):navigator.userAgent}function c(){return!/^((?!chrome|android).)*safari/i.test(f())}function p(e,o,i){void 0===o&&(o=!1),void 0===i&&(i=!1);var a=e.getBoundingClientRect(),f=1,p=1;o&&r(e)&&(f=e.offsetWidth>0&&s(a.width)/e.offsetWidth||1,p=e.offsetHeight>0&&s(a.height)/e.offsetHeight||1);var u=(n(e)?t(e):window).visualViewport,l=!c()&&i,d=(a.left+(l&&u?u.offsetLeft:0))/f,h=(a.top+(l&&u?u.offsetTop:0))/p,m=a.width/f,v=a.height/p;return{width:m,height:v,top:h,right:d+m,bottom:h+v,left:d,x:d,y:h}}function u(e){var n=t(e);return{scrollLeft:n.pageXOffset,scrollTop:n.pageYOffset}}function l(e){return e?(e.nodeName||"").toLowerCase():null}function d(e){return((n(e)?e.ownerDocument:e.document)||window.document).documentElement}function h(e){return p(d(e)).left+u(e).scrollLeft}function m(e){return t(e).getComputedStyle(e)}function v(e){var t=m(e),n=t.overflow,r=t.overflowX,o=t.overflowY;return/auto|scroll|overlay|hidden/.test(n+o+r)}function y(e,n,o){void 0===o&&(o=!1);var i,a,f=r(n),c=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),m=d(n),y=p(e,c,o),g={scrollLeft:0,scrollTop:0},b={x:0,y:0};return(f||!f&&!o)&&(("body"!==l(n)||v(m))&&(g=(i=n)!==t(i)&&r(i)?{scrollLeft:(a=i).scrollLeft,scrollTop:a.scrollTop}:u(i)),r(n)?((b=p(n,!0)).x+=n.clientLeft,b.y+=n.clientTop):m&&(b.x=h(m))),{x:y.left+g.scrollLeft-b.x,y:y.top+g.scrollTop-b.y,width:y.width,height:y.height}}function g(e){var t=p(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 b(e){return"html"===l(e)?e:e.assignedSlot||e.parentNode||(o(e)?e.host:null)||d(e)}function x(e){return["html","body","#document"].indexOf(l(e))>=0?e.ownerDocument.body:r(e)&&v(e)?e:x(b(e))}function w(e,n){var r;void 0===n&&(n=[]);var o=x(e),i=o===(null==(r=e.ownerDocument)?void 0:r.body),a=t(o),s=i?[a].concat(a.visualViewport||[],v(o)?o:[]):o,f=n.concat(s);return i?f:f.concat(w(b(s)))}function O(e){return["table","td","th"].indexOf(l(e))>=0}function j(e){return r(e)&&"fixed"!==m(e).position?e.offsetParent:null}function E(e){for(var n=t(e),i=j(e);i&&O(i)&&"static"===m(i).position;)i=j(i);return i&&("html"===l(i)||"body"===l(i)&&"static"===m(i).position)?n:i||function(e){var t=/firefox/i.test(f());if(/Trident/i.test(f())&&r(e)&&"fixed"===m(e).position)return null;var n=b(e);for(o(n)&&(n=n.host);r(n)&&["html","body"].indexOf(l(n))<0;){var i=m(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 D="top",A="bottom",L="right",P="left",M="auto",k=[D,A,L,P],W="start",B="end",H="viewport",T="popper",R=k.reduce((function(e,t){return e.concat([t+"-"+W,t+"-"+B])}),[]),S=[].concat(k,[M]).reduce((function(e,t){return e.concat([t,t+"-"+W,t+"-"+B])}),[]),V=["beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite"];function q(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 N(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 I(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function _(e,r,o){return r===H?I(function(e,n){var r=t(e),o=d(e),i=r.visualViewport,a=o.clientWidth,s=o.clientHeight,f=0,p=0;if(i){a=i.width,s=i.height;var u=c();(u||!u&&"fixed"===n)&&(f=i.offsetLeft,p=i.offsetTop)}return{width:a,height:s,x:f+h(e),y:p}}(e,o)):n(r)?function(e,t){var n=p(e,!1,"fixed"===t);return n.top=n.top+e.clientTop,n.left=n.left+e.clientLeft,n.bottom=n.top+e.clientHeight,n.right=n.left+e.clientWidth,n.width=e.clientWidth,n.height=e.clientHeight,n.x=n.left,n.y=n.top,n}(r,o):I(function(e){var t,n=d(e),r=u(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+h(e),c=-r.scrollTop;return"rtl"===m(o||n).direction&&(f+=i(n.clientWidth,o?o.clientWidth:0)-a),{width:a,height:s,x:f,y:c}}(d(e)))}function F(e,t,o,s){var f="clippingParents"===t?function(e){var t=w(b(e)),o=["absolute","fixed"].indexOf(m(e).position)>=0&&r(e)?E(e):e;return n(o)?t.filter((function(e){return n(e)&&N(e,o)&&"body"!==l(e)})):[]}(e):[].concat(t),c=[].concat(f,[o]),p=c[0],u=c.reduce((function(t,n){var r=_(e,n,s);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}),_(e,p,s));return u.width=u.right-u.left,u.height=u.bottom-u.top,u.x=u.left,u.y=u.top,u}function U(e){return e.split("-")[1]}function z(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}function X(e){var t,n=e.reference,r=e.element,o=e.placement,i=o?C(o):null,a=o?U(o):null,s=n.x+n.width/2-r.width/2,f=n.y+n.height/2-r.height/2;switch(i){case D:t={x:s,y:n.y-r.height};break;case A:t={x:s,y:n.y+n.height};break;case L:t={x:n.x+n.width,y:f};break;case P:t={x:n.x-r.width,y:f};break;default:t={x:n.x,y:n.y}}var c=i?z(i):null;if(null!=c){var p="y"===c?"height":"width";switch(a){case W:t[c]=t[c]-(n[p]/2-r[p]/2);break;case B:t[c]=t[c]+(n[p]/2-r[p]/2)}}return t}function Y(e){return Object.assign({},{top:0,right:0,bottom:0,left:0},e)}function G(e,t){return t.reduce((function(t,n){return t[n]=e,t}),{})}function J(e,t){void 0===t&&(t={});var r=t,o=r.placement,i=void 0===o?e.placement:o,a=r.strategy,s=void 0===a?e.strategy:a,f=r.boundary,c=void 0===f?"clippingParents":f,u=r.rootBoundary,l=void 0===u?H:u,h=r.elementContext,m=void 0===h?T:h,v=r.altBoundary,y=void 0!==v&&v,g=r.padding,b=void 0===g?0:g,x=Y("number"!=typeof b?b:G(b,k)),w=m===T?"reference":T,O=e.rects.popper,j=e.elements[y?w:m],E=F(n(j)?j:j.contextElement||d(e.elements.popper),c,l,s),P=p(e.elements.reference),M=X({reference:P,element:O,strategy:"absolute",placement:i}),W=I(Object.assign({},O,M)),B=m===T?W:P,R={top:E.top-B.top+x.top,bottom:B.bottom-E.bottom+x.bottom,left:E.left-B.left+x.left,right:B.right-E.right+x.right},S=e.modifiersData.offset;if(m===T&&S){var V=S[i];Object.keys(R).forEach((function(e){var t=[L,A].indexOf(e)>=0?1:-1,n=[D,A].indexOf(e)>=0?"y":"x";R[e]+=V[n]*t}))}return R}var K={placement:"bottom",modifiers:[],strategy:"absolute"};function Q(){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,[P,L].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}},se={left:"right",right:"left",bottom:"top",top:"bottom"};function fe(e){return e.replace(/left|right|bottom|top/g,(function(e){return se[e]}))}var ce={start:"end",end:"start"};function pe(e){return e.replace(/start|end/g,(function(e){return ce[e]}))}function ue(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?S:f,p=U(r),u=p?s?R:R.filter((function(e){return U(e)===p})):k,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]=J(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 le={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,y=C(v),g=f||(y===v||!h?[fe(v)]:function(e){if(C(e)===M)return[];var t=fe(e);return[pe(e),t,pe(t)]}(v)),b=[v].concat(g).reduce((function(e,n){return e.concat(C(n)===M?ue(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,j=!0,E=b[0],k=0;k=0,S=R?"width":"height",V=J(t,{placement:B,boundary:p,rootBoundary:u,altBoundary:l,padding:c}),q=R?T?L:P:T?A:D;x[S]>w[S]&&(q=fe(q));var N=fe(q),I=[];if(i&&I.push(V[H]<=0),s&&I.push(V[q]<=0,V[N]<=0),I.every((function(e){return e}))){E=B,j=!1;break}O.set(B,I)}if(j)for(var _=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 E=t,"break"},F=h?3:1;F>0;F--){if("break"===_(F))break}t.placement!==E&&(t.modifiersData[r]._skip=!0,t.placement=E,t.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function de(e,t,n){return i(e,a(t,n))}var he={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,v=n.tetherOffset,y=void 0===v?0:v,b=J(t,{boundary:p,rootBoundary:u,padding:d,altBoundary:l}),x=C(t.placement),w=U(t.placement),O=!w,j=z(x),M="x"===j?"y":"x",k=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,V={x:0,y:0};if(k){if(s){var q,N="y"===j?D:P,I="y"===j?A:L,_="y"===j?"height":"width",F=k[j],X=F+b[N],Y=F-b[I],G=m?-H[_]/2:0,K=w===W?B[_]:H[_],Q=w===W?-H[_]:-B[_],Z=t.elements.arrow,$=m&&Z?g(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=de(0,B[_],$[_]),oe=O?B[_]/2-G-re-te-R.mainAxis:K-re-te-R.mainAxis,ie=O?-B[_]/2+G+re+ne+R.mainAxis:Q+re+ne+R.mainAxis,ae=t.elements.arrow&&E(t.elements.arrow),se=ae?"y"===j?ae.clientTop||0:ae.clientLeft||0:0,fe=null!=(q=null==S?void 0:S[j])?q:0,ce=F+ie-fe,pe=de(m?a(X,F+oe-fe-se):X,F,m?i(Y,ce):Y);k[j]=pe,V[j]=pe-F}if(c){var ue,le="x"===j?D:P,he="x"===j?A:L,me=k[M],ve="y"===M?"height":"width",ye=me+b[le],ge=me-b[he],be=-1!==[D,P].indexOf(x),xe=null!=(ue=null==S?void 0:S[M])?ue:0,we=be?ye:me-B[ve]-H[ve]-xe+R.altAxis,Oe=be?me+B[ve]+H[ve]-xe-R.altAxis:ge,je=m&&be?function(e,t,n){var r=de(e,t,n);return r>n?n:r}(we,me,Oe):de(m?we:ye,me,m?Oe:ge);k[M]=je,V[M]=je-me}t.modifiersData[r]=V}},requiresIfExists:["offset"]};var me={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=z(s),c=[P,L].indexOf(s)>=0?"height":"width";if(i&&a){var p=function(e,t){return Y("number"!=typeof(e="function"==typeof e?e(Object.assign({},t.rects,{placement:t.placement})):e)?e:G(e,k))}(o.padding,n),u=g(i),l="y"===f?D:P,d="y"===f?A:L,h=n.rects.reference[c]+n.rects.reference[f]-a[f]-n.rects.popper[c],m=a[f]-n.rects.reference[f],v=E(i),y=v?"y"===f?v.clientHeight||0:v.clientWidth||0:0,b=h/2-m/2,x=p[l],w=y-u[c]-p[d],O=y/2-u[c]/2+b,j=de(x,O,w),M=f;n.modifiersData[r]=((t={})[M]=j,t.centerOffset=j-O,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)))&&N(t.elements.popper,r)&&(t.elements.arrow=r)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function ve(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 ye(e){return[D,L,A,P].some((function(t){return e[t]>=0}))}var ge={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=J(t,{elementContext:"reference"}),s=J(t,{altBoundary:!0}),f=ve(a,r),c=ve(s,o,i),p=ye(f),u=ye(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})}},be=Z({defaultModifiers:[ee,te,oe,ie]}),xe=[ee,te,oe,ie,ae,le,he,me,ge],we=Z({defaultModifiers:xe});e.applyStyles=ie,e.arrow=me,e.computeStyles=oe,e.createPopper=we,e.createPopperLite=be,e.defaultModifiers=xe,e.detectOverflow=J,e.eventListeners=ee,e.flip=le,e.hide=ge,e.offset=ae,e.popperGenerator=Z,e.popperOffsets=te,e.preventOverflow=he,Object.defineProperty(e,"__esModule",{value:!0})})); + diff --git a/_site/site_libs/quarto-html/quarto-syntax-highlighting.css b/_site/site_libs/quarto-html/quarto-syntax-highlighting.css new file mode 100644 index 0000000..d9fd98f --- /dev/null +++ b/_site/site_libs/quarto-html/quarto-syntax-highlighting.css @@ -0,0 +1,203 @@ +/* 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; + font-style: inherit; +} + +code span.at { + color: #657422; + font-style: inherit; +} + +code span.ss { + color: #20794D; + font-style: inherit; +} + +code span.an { + color: #5E5E5E; + font-style: inherit; +} + +code span.fu { + color: #4758AB; + font-style: inherit; +} + +code span.st { + color: #20794D; + font-style: inherit; +} + +code span.cf { + color: #003B4F; + font-style: inherit; +} + +code span.op { + color: #5E5E5E; + font-style: inherit; +} + +code span.er { + color: #AD0000; + font-style: inherit; +} + +code span.bn { + color: #AD0000; + font-style: inherit; +} + +code span.al { + color: #AD0000; + font-style: inherit; +} + +code span.va { + color: #111111; + font-style: inherit; +} + +code span.bu { + font-style: inherit; +} + +code span.ex { + font-style: inherit; +} + +code span.pp { + color: #AD0000; + font-style: inherit; +} + +code span.in { + color: #5E5E5E; + font-style: inherit; +} + +code span.vs { + color: #20794D; + font-style: inherit; +} + +code span.wa { + color: #5E5E5E; + font-style: italic; +} + +code span.do { + color: #5E5E5E; + font-style: italic; +} + +code span.im { + color: #00769E; + font-style: inherit; +} + +code span.ch { + color: #20794D; + font-style: inherit; +} + +code span.dt { + color: #AD0000; + font-style: inherit; +} + +code span.fl { + color: #AD0000; + font-style: inherit; +} + +code span.co { + color: #5E5E5E; + font-style: inherit; +} + +code span.cv { + color: #5E5E5E; + font-style: italic; +} + +code span.cn { + color: #8f5902; + font-style: inherit; +} + +code span.sc { + color: #5E5E5E; + font-style: inherit; +} + +code span.dv { + color: #AD0000; + font-style: inherit; +} + +code span.kw { + color: #003B4F; + font-style: inherit; +} + +.prevent-inlining { + content: " { + // Find any conflicting margin elements and add margins to the + // top to prevent overlap + const marginChildren = window.document.querySelectorAll( + ".column-margin.column-container > *, .margin-caption, .aside" + ); + + let lastBottom = 0; + for (const marginChild of marginChildren) { + if (marginChild.offsetParent !== null) { + // clear the top margin so we recompute it + marginChild.style.marginTop = null; + const top = marginChild.getBoundingClientRect().top + window.scrollY; + if (top < lastBottom) { + const marginChildStyle = window.getComputedStyle(marginChild); + const marginBottom = parseFloat(marginChildStyle["marginBottom"]); + const margin = lastBottom - top + marginBottom; + marginChild.style.marginTop = `${margin}px`; + } + const styles = window.getComputedStyle(marginChild); + const marginTop = parseFloat(styles["marginTop"]); + lastBottom = top + marginChild.getBoundingClientRect().height + marginTop; + } + } +}; + +window.document.addEventListener("DOMContentLoaded", function (_event) { + // Recompute the position of margin elements anytime the body size changes + if (window.ResizeObserver) { + const resizeObserver = new window.ResizeObserver( + throttle(() => { + layoutMarginEls(); + if ( + window.document.body.getBoundingClientRect().width < 990 && + isReaderMode() + ) { + quartoToggleReader(); + } + }, 50) + ); + resizeObserver.observe(window.document.body); + } + + const tocEl = window.document.querySelector('nav.toc-active[role="doc-toc"]'); + const sidebarEl = window.document.getElementById("quarto-sidebar"); + const leftTocEl = window.document.getElementById("quarto-sidebar-toc-left"); + const marginSidebarEl = window.document.getElementById( + "quarto-margin-sidebar" + ); + // function to determine whether the element has a previous sibling that is active + const prevSiblingIsActiveLink = (el) => { + 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; + let elRect; + + return (hiddenRegions) => { + if (el === null) { + return; + } + + // Find the last element of the TOC + const lastChildEl = el.lastElementChild; + + if (lastChildEl) { + // Converts the sidebar to a menu + const convertToMenu = () => { + for (const child of el.children) { + child.style.opacity = 0; + child.style.overflow = "hidden"; + } + + nexttick(() => { + 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.textContent || 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"; + const positionToggle = () => { + // position the element (top left of parent, same width as parent) + if (!elRect) { + elRect = el.getBoundingClientRect(); + } + toggleContainer.style.left = `${elRect.left}px`; + toggleContainer.style.top = `${elRect.top}px`; + toggleContainer.style.width = `${elRect.width}px`; + }; + positionToggle(); + + 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; + } + }; + + // 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(() => { + elRect = undefined; + positionToggle(); + }, 50) + ); + + window.addEventListener("quarto-hrChanged", () => { + elRect = undefined; + }); + + // 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 { + // Find the top and bottom o the element that is being managed + const elTop = el.offsetTop; + const elBottom = + elTop + lastChildEl.offsetTop + lastChildEl.offsetHeight; + + 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; + } + } + } + } + }; + }; + + const tabEls = document.querySelectorAll('a[data-bs-toggle="tab"]'); + for (const tabEl of tabEls) { + const id = tabEl.getAttribute("data-bs-target"); + if (id) { + const columnEl = document.querySelector( + `${id} .column-margin, .tabset-margin-content` + ); + if (columnEl) + tabEl.addEventListener("shown.bs.tab", function (event) { + const el = event.srcElement; + if (el) { + const visibleCls = `${el.id}-margin-content`; + // walk up until we find a parent tabset + let panelTabsetEl = el.parentElement; + while (panelTabsetEl) { + if (panelTabsetEl.classList.contains("panel-tabset")) { + break; + } + panelTabsetEl = panelTabsetEl.parentElement; + } + + if (panelTabsetEl) { + const prevSib = panelTabsetEl.previousElementSibling; + if ( + prevSib && + prevSib.classList.contains("tabset-margin-container") + ) { + const childNodes = prevSib.querySelectorAll( + ".tabset-margin-content" + ); + for (const childEl of childNodes) { + if (childEl.classList.contains(visibleCls)) { + childEl.classList.remove("collapse"); + } else { + childEl.classList.add("collapse"); + } + } + } + } + } + + layoutMarginEls(); + }); + } + } + + // 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 boundRect = el.getBoundingClientRect(); + const top = + boundRect.top + + document.documentElement.scrollTop - + kOverlapPaddingSize; + return { + top, + bottom: top + el.scrollHeight + 2 * kOverlapPaddingSize, + }; + }); + } + + let hasObserved = false; + const visibleItemObserver = (els) => { + let visibleElements = [...els]; + const intersectionObserver = new IntersectionObserver( + (entries, _observer) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + if (visibleElements.indexOf(entry.target) === -1) { + visibleElements.push(entry.target); + } + } else { + visibleElements = visibleElements.filter((visibleEntry) => { + return visibleEntry !== entry; + }); + } + }); + + if (!hasObserved) { + hideOverlappedSidebars(); + } + hasObserved = true; + }, + {} + ); + els.forEach((el) => { + intersectionObserver.observe(el); + }); + + return { + getVisibleEntries: () => { + return visibleElements; + }, + }; + }; + + const rightElementObserver = visibleItemObserver(rightSideConflictEls); + const leftElementObserver = visibleItemObserver(leftSideConflictEls); + + const hideOverlappedSidebars = () => { + marginScrollVisibility(toRegions(rightElementObserver.getVisibleEntries())); + sidebarScrollVisiblity(toRegions(leftElementObserver.getVisibleEntries())); + if (tocLeftScrollVisibility) { + tocLeftScrollVisibility( + toRegions(leftElementObserver.getVisibleEntries()) + ); + } + }; + + 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; + + const tocOpenDepthStr = tocEl?.getAttribute("data-toc-expanded"); + const tocOpenDepth = tocOpenDepthStr ? Number(tocOpenDepthStr) : 1; + + // 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 (tocOpenDepth === -1 && depth > 1) { + el.classList.add("collapse"); + } else if ( + depth <= tocOpenDepth || + 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/site_libs/quarto-html/tippy.css b/_site/site_libs/quarto-html/tippy.css new file mode 100644 index 0000000..e6ae635 --- /dev/null +++ b/_site/site_libs/quarto-html/tippy.css @@ -0,0 +1 @@ +.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/site_libs/quarto-html/tippy.umd.min.js b/_site/site_libs/quarto-html/tippy.umd.min.js new file mode 100644 index 0000000..ca292be --- /dev/null +++ b/_site/site_libs/quarto-html/tippy.umd.min.js @@ -0,0 +1,2 @@ +!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/site_libs/quarto-nav/headroom.min.js b/_site/site_libs/quarto-nav/headroom.min.js new file mode 100644 index 0000000..b08f1df --- /dev/null +++ b/_site/site_libs/quarto-nav/headroom.min.js @@ -0,0 +1,7 @@ +/*! + * 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/site_libs/quarto-nav/quarto-nav.js b/_site/site_libs/quarto-nav/quarto-nav.js new file mode 100644 index 0000000..ebfc262 --- /dev/null +++ b/_site/site_libs/quarto-nav/quarto-nav.js @@ -0,0 +1,288 @@ +const headroomChanged = new CustomEvent("quarto-hrChanged", { + detail: {}, + bubbles: true, + cancelable: false, + composed: false, +}); + +window.document.addEventListener("DOMContentLoaded", function () { + let init = false; + + // Manage the back to top button, if one is present. + let lastScrollTop = window.pageYOffset || document.documentElement.scrollTop; + const scrollDownBuffer = 5; + const scrollUpBuffer = 35; + const btn = document.getElementById("quarto-back-to-top"); + const hideBackToTop = () => { + btn.style.display = "none"; + }; + const showBackToTop = () => { + btn.style.display = "inline-block"; + }; + if (btn) { + window.document.addEventListener( + "scroll", + function () { + const currentScrollTop = + window.pageYOffset || document.documentElement.scrollTop; + + // Shows and hides the button 'intelligently' as the user scrolls + if (currentScrollTop - scrollDownBuffer > lastScrollTop) { + hideBackToTop(); + lastScrollTop = currentScrollTop <= 0 ? 0 : currentScrollTop; + } else if (currentScrollTop < lastScrollTop - scrollUpBuffer) { + showBackToTop(); + lastScrollTop = currentScrollTop <= 0 ? 0 : currentScrollTop; + } + + // Show the button at the bottom, hides it at the top + if (currentScrollTop <= 0) { + hideBackToTop(); + } else if ( + window.innerHeight + currentScrollTop >= + document.body.offsetHeight + ) { + showBackToTop(); + } + }, + 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 dashboardOffset() { + const dashboardNavEl = window.document.getElementById( + "quarto-dashboard-header" + ); + if (dashboardNavEl !== null) { + return dashboardNavEl.clientHeight; + } else { + return 0; + } + } + + function updateDocumentOffsetWithoutAnimation() { + updateDocumentOffset(false); + } + + function updateDocumentOffset(animated) { + // set body offset + const topOffset = headerOffset(); + const bodyOffset = topOffset + footerOffset() + dashboardOffset(); + 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; + } + }; + } + + window.addEventListener( + "hashchange", + function (e) { + if ( + getComputedStyle(document.documentElement).scrollBehavior !== "smooth" + ) { + window.scrollTo(0, window.pageYOffset - headerOffset()); + } + }, + false + ); + + // Observe size changed for the header + const headerEl = window.document.querySelector("header.fixed-top"); + if (headerEl && window.ResizeObserver) { + const observer = new window.ResizeObserver(() => { + setTimeout(updateDocumentOffsetWithoutAnimation, 0); + }); + 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++) { + if (links[i].href) { + 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, a.quarto-navigation-tool, a.quarto-navbar-tools, a.quarto-navbar-tools-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/site_libs/quarto-search/autocomplete.umd.js b/_site/site_libs/quarto-search/autocomplete.umd.js new file mode 100644 index 0000000..ae0063a --- /dev/null +++ b/_site/site_libs/quarto-search/autocomplete.umd.js @@ -0,0 +1,3 @@ +/*! @algolia/autocomplete-js 1.11.1 | 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,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var r,o,i,u,a=[],l=!0,c=!1;try{if(i=(n=n.call(e)).next,0===t){if(Object(n)!==n)return;l=!1}else for(;!(l=(r=i.call(n)).done)&&(a.push(r.value),a.length!==t);l=!0);}catch(e){c=!0,o=e}finally{try{if(!l&&null!=n.return&&(u=n.return(),Object(u)!==u))return}finally{if(c)throw o}}return a}}(e,t)||c(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function l(e){return function(e){if(Array.isArray(e))return s(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||c(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){if(e){if("string"==typeof e)return s(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?s(e,t):void 0}}function s(e,t){(null==t||t>e.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);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}function x(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 t=1;t1&&void 0!==arguments[1]?arguments[1]:20,n=[],r=0;r=3||2===n&&r>=4||1===n&&r>=10);function i(t,n,r){if(o&&void 0!==r){var i=r[0].__autocomplete_algoliaCredentials,u={"X-Algolia-Application-Id":i.appId,"X-Algolia-API-Key":i.apiKey};e.apply(void 0,[t].concat(D(n),[{headers:u}]))}else e.apply(void 0,[t].concat(D(n)))}return{init:function(t,n){e("init",{appId:t,apiKey:n})},setUserToken:function(t){e("setUserToken",t)},clickedObjectIDsAfterSearch:function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&i("clickedObjectIDsAfterSearch",B(t),t[0].items)},clickedObjectIDs:function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&i("clickedObjectIDs",B(t),t[0].items)},clickedFilters:function(){for(var t=arguments.length,n=new Array(t),r=0;r0&&e.apply(void 0,["clickedFilters"].concat(n))},convertedObjectIDsAfterSearch:function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&i("convertedObjectIDsAfterSearch",B(t),t[0].items)},convertedObjectIDs:function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&i("convertedObjectIDs",B(t),t[0].items)},convertedFilters:function(){for(var t=arguments.length,n=new Array(t),r=0;r0&&e.apply(void 0,["convertedFilters"].concat(n))},viewedObjectIDs:function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&t.reduce((function(e,t){var n=t.items,r=k(t,A);return[].concat(D(e),D(q(N(N({},r),{},{objectIDs:(null==n?void 0:n.map((function(e){return e.objectID})))||r.objectIDs})).map((function(e){return{items:n,payload:e}}))))}),[]).forEach((function(e){var t=e.items;return i("viewedObjectIDs",[e.payload],t)}))},viewedFilters:function(){for(var t=arguments.length,n=new Array(t),r=0;r0&&e.apply(void 0,["viewedFilters"].concat(n))}}}function F(e){var t=e.items.reduce((function(e,t){var n;return e[t.__autocomplete_indexName]=(null!==(n=e[t.__autocomplete_indexName])&&void 0!==n?n:[]).concat(t),e}),{});return Object.keys(t).map((function(e){return{index:e,items:t[e],algoliaSource:["autocomplete"]}}))}function L(e){return e.objectID&&e.__autocomplete_indexName&&e.__autocomplete_queryID}function U(e){return U="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},U(e)}function M(e){return function(e){if(Array.isArray(e))return H(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 H(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 H(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 H(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n0&&z({onItemsChange:r,items:n,insights:a,state:t}))}}),0);return{name:"aa.algoliaInsightsPlugin",subscribe:function(e){var t=e.setContext,n=e.onSelect,r=e.onActive;function l(e){t({algoliaInsightsPlugin:{__algoliaSearchParameters:W({clickAnalytics:!0},e?{userToken:e}:{}),insights:a}})}u("addAlgoliaAgent","insights-plugin"),l(),u("onUserTokenChange",l),u("getUserToken",null,(function(e,t){l(t)})),n((function(e){var t=e.item,n=e.state,r=e.event,i=e.source;L(t)&&o({state:n,event:r,insights:a,item:t,insightsEvents:[W({eventName:"Item Selected"},j({item:t,items:i.getItems().filter(L)}))]})})),r((function(e){var t=e.item,n=e.source,r=e.state,o=e.event;L(t)&&i({state:r,event:o,insights:a,item:t,insightsEvents:[W({eventName:"Item Active"},j({item:t,items:n.getItems().filter(L)}))]})}))},onStateChange:function(e){var t=e.state;c({state:t})},__autocomplete_pluginOptions:e}}function J(e,t){var n=t;return{then:function(t,r){return J(e.then(Y(t,n,e),Y(r,n,e)),n)},catch:function(t){return J(e.catch(Y(t,n,e)),n)},finally:function(t){return t&&n.onCancelList.push(t),J(e.finally(Y(t&&function(){return n.onCancelList=[],t()},n,e)),n)},cancel:function(){n.isCanceled=!0;var e=n.onCancelList;n.onCancelList=[],e.forEach((function(e){e()}))},isCanceled:function(){return!0===n.isCanceled}}}function X(e){return J(e,{isCanceled:!1,onCancelList:[]})}function Y(e,t,n){return e?function(n){return t.isCanceled?n:e(n)}:n}function Z(e,t,n,r){if(!n)return null;if(e<0&&(null===t||null!==r&&0===t))return n+e;var o=(null===t?-1:t)+e;return o<=-1||o>=n?null===r?null:0:o}function ee(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 te(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=new Array(t);n0},reshape:function(e){return e.sources}},e),{},{id:null!==(n=e.id)&&void 0!==n?n:d(),plugins:o,initialState:he({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(ye(o.map((function(e){return e.getSources}))),[e.getSources]).filter(Boolean).map((function(e){return function(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={getItemInputValue:function(e){return e.state.query},getItemUrl:function(){},onSelect:function(e){(0,e.setIsOpen)(!1)},onActive:O,onResolve:O};Object.keys(t).forEach((function(e){t[e].__default=!0}));var r=te(te({},t),e);return Promise.resolve(r)})))}))}(e,n)}))).then((function(e){return m(e)})).then((function(e){return e.map((function(e){return he(he({},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)}))},onResolve:function(n){e.onResolve(n),t.forEach((function(e){var t;return null===(t=e.onResolve)||void 0===t?void 0:t.call(e,n)}))}})}))}))},navigator:he({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)})}function Se(e){return Se="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Se(e)}function je(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 Pe(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}var He,Ve,We,Ke=null,Qe=(He=-1,Ve=-1,We=void 0,function(e){var t=++He;return Promise.resolve(e).then((function(e){return We&&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 et(e){return et="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},et(e)}var tt=["props","refresh","store"],nt=["inputElement","formElement","panelElement"],rt=["inputElement"],ot=["inputElement","maxLength"],it=["source"],ut=["item","source"];function at(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 lt(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 ft(e){var t=e.props,n=e.refresh,r=e.store,o=st(e,tt);return{getEnvironmentProps:function(e){var n=e.inputElement,o=e.formElement,i=e.panelElement;function u(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())}return lt({onTouchStart:u,onMouseDown:u,onTouchMove:function(e){!1!==r.getState().isOpen&&n===t.environment.document.activeElement&&e.target!==n&&n.blur()}},st(e,nt))},getRootProps:function(e){return lt({role:"combobox","aria-expanded":r.getState().isOpen,"aria-haspopup":"listbox","aria-owns":r.getState().isOpen?r.getState().collections.map((function(e){var n=e.source;return ie(t.id,"list",n)})).join(" "):void 0,"aria-labelledby":ie(t.id,"label")},e)},getFormProps:function(e){return e.inputElement,lt({action:"",noValidate:!0,role:"search",onSubmit:function(i){var u;i.preventDefault(),t.onSubmit(lt({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(lt({event:i,refresh:n,state:r.getState()},o)),r.dispatch("reset",null),null===(u=e.inputElement)||void 0===u||u.focus()}},st(e,rt))},getLabelProps:function(e){return lt({htmlFor:ie(t.id,"input"),id:ie(t.id,"label")},e)},getInputProps:function(e){var i;function u(e){(t.openOnFocus||Boolean(r.getState().query))&&$e(lt({event:e,props:t,query:r.getState().completion||r.getState().query,refresh:n,store:r},o)),r.dispatch("focus",null)}var a=e||{};a.inputElement;var l=a.maxLength,c=void 0===l?512:l,s=st(a,ot),f=oe(r.getState()),p=function(e){return Boolean(e&&e.match(ue))}((null===(i=t.environment.navigator)||void 0===i?void 0:i.userAgent)||""),m=t.enterKeyHint||(null!=f&&f.itemUrl&&!p?"go":"search");return lt({"aria-autocomplete":"both","aria-activedescendant":r.getState().isOpen&&null!==r.getState().activeItemId?ie(t.id,"item-".concat(r.getState().activeItemId),null==f?void 0:f.source):void 0,"aria-controls":r.getState().isOpen?r.getState().collections.map((function(e){var n=e.source;return ie(t.id,"list",n)})).join(" "):void 0,"aria-labelledby":ie(t.id,"label"),value:r.getState().completion||r.getState().query,id:ie(t.id,"input"),autoComplete:"off",autoCorrect:"off",autoCapitalize:"off",enterKeyHint:m,spellCheck:"false",autoFocus:t.autoFocus,placeholder:t.placeholder,maxLength:c,type:"search",onChange:function(e){$e(lt({event:e,props:t,query:e.currentTarget.value.slice(0,c),refresh:n,store:r},o))},onKeyDown:function(e){!function(e){var t=e.event,n=e.props,r=e.refresh,o=e.store,i=Ze(e,Ge);if("ArrowUp"===t.key||"ArrowDown"===t.key){var u=function(){var e=oe(o.getState()),t=n.environment.document.getElementById(ie(n.id,"item-".concat(o.getState().activeItemId),null==e?void 0:e.source));t&&(t.scrollIntoViewIfNeeded?t.scrollIntoViewIfNeeded(!1):t.scrollIntoView(!1))},a=function(){var e=oe(o.getState());if(null!==o.getState().activeItemId&&e){var n=e.item,u=e.itemInputValue,a=e.itemUrl,l=e.source;l.onActive(Xe({event:t,item:n,itemInputValue:u,itemUrl:a,refresh:r,source:l,state:o.getState()},i))}};t.preventDefault(),!1===o.getState().isOpen&&(n.openOnFocus||Boolean(o.getState().query))?$e(Xe({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("Tab"===t.key)o.dispatch("blur",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 void(n.debug||o.pendingRequests.cancelAll());t.preventDefault();var l=oe(o.getState()),c=l.item,s=l.itemInputValue,f=l.itemUrl,p=l.source;if(t.metaKey||t.ctrlKey)void 0!==f&&(p.onSelect(Xe({event:t,item:c,itemInputValue:s,itemUrl:f,refresh:r,source:p,state:o.getState()},i)),n.navigator.navigateNewTab({itemUrl:f,item:c,state:o.getState()}));else if(t.shiftKey)void 0!==f&&(p.onSelect(Xe({event:t,item:c,itemInputValue:s,itemUrl:f,refresh:r,source:p,state:o.getState()},i)),n.navigator.navigateNewWindow({itemUrl:f,item:c,state:o.getState()}));else if(t.altKey);else{if(void 0!==f)return p.onSelect(Xe({event:t,item:c,itemInputValue:s,itemUrl:f,refresh:r,source:p,state:o.getState()},i)),void n.navigator.navigate({itemUrl:f,item:c,state:o.getState()});$e(Xe({event:t,nextState:{isOpen:!1},props:n,query:s,refresh:r,store:o},i)).then((function(){p.onSelect(Xe({event:t,item:c,itemInputValue:s,itemUrl:f,refresh:r,source:p,state:o.getState()},i))}))}}}(lt({event:e,props:t,refresh:n,store:r},o))},onFocus:u,onBlur:O,onClick:function(n){e.inputElement!==t.environment.document.activeElement||r.getState().isOpen||u(n)}},s)},getPanelProps:function(e){return lt({onMouseDown:function(e){e.preventDefault()},onMouseLeave:function(){r.dispatch("mouseleave",null)}},e)},getListProps:function(e){var n=e||{},r=n.source,o=st(n,it);return lt({role:"listbox","aria-labelledby":ie(t.id,"label"),id:ie(t.id,"list",r)},o)},getItemProps:function(e){var i=e.item,u=e.source,a=st(e,ut);return lt({id:ie(t.id,"item-".concat(i.__autocomplete_id),u),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=oe(r.getState());if(null!==r.getState().activeItemId&&t){var u=t.item,a=t.itemInputValue,l=t.itemUrl,c=t.source;c.onActive(lt({event:e,item:u,itemInputValue:a,itemUrl:l,refresh:n,source:c,state:r.getState()},o))}}},onMouseDown:function(e){e.preventDefault()},onClick:function(e){var a=u.getItemInputValue({item:i,state:r.getState()}),l=u.getItemUrl({item:i,state:r.getState()});(l?Promise.resolve():$e(lt({event:e,nextState:{isOpen:!1},props:t,query:a,refresh:n,store:r},o))).then((function(){u.onSelect(lt({event:e,item:i,itemInputValue:a,itemUrl:l,refresh:n,source:u,state:r.getState()},o))}))}},a)}}}function pt(e){return pt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},pt(e)}function mt(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 vt(e){for(var t=1;t=5&&((o||!e&&5===r)&&(u.push(r,0,o,n),r=6),e&&(u.push(r,e,0,n),r=6)),o=""},l=0;l"===t?(r=1,o=""):o=t+o[0]:i?t===i?i="":o+=t:'"'===t||"'"===t?i=t:">"===t?(a(),r=1):r&&("="===t?(r=5,n=o,o=""):"/"===t&&(r<5||">"===e[l][c+1])?(a(),3===r&&(u=u[0]),r=u,(u=u[0]).push(2,0,r),r=0):" "===t||"\t"===t||"\n"===t||"\r"===t?(a(),r=2):o+=t),3===r&&"!--"===o&&(r=4,u=u[0])}return a(),u}(e)),t),arguments,[])).length>1?t:t[0]}var kt=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 xt(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 Nt(){for(var e=arguments.length,t=new Array(e),n=0;n2&&(u.children=arguments.length>3?Jt.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 sn(e,u,r,o,null)}function sn(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?++Yt:o};return null==o&&null!=Xt.vnode&&Xt.vnode(i),i}function fn(e){return e.children}function pn(e,t){this.props=e,this.context=t}function mn(e,t){if(null==t)return e.__?mn(e.__,e.__.__k.indexOf(e)+1):null;for(var n;tt&&Zt.sort(nn));yn.__r=0}function bn(e,t,n,r,o,i,u,a,l,c){var s,f,p,m,v,d,y,b=r&&r.__k||on,g=b.length;for(n.__k=[],s=0;s0?sn(m.type,m.props,m.key,m.ref?m.ref:null,m.__v):m)){if(m.__=n,m.__b=n.__b+1,null===(p=b[s])||p&&m.key==p.key&&m.type===p.type)b[s]=void 0;else for(f=0;f=0;t--)if((n=e.__k[t])&&(r=On(n)))return r;return null}function _n(e,t,n){"-"===t[0]?e.setProperty(t,null==n?"":n):e[t]=null==n?"":"number"!=typeof n||un.test(t)?n:n+"px"}function Sn(e,t,n,r,o){var i;e:if("style"===t)if("string"==typeof n)e.style.cssText=n;else{if("string"==typeof r&&(e.style.cssText=r=""),r)for(t in r)n&&t in n||_n(e.style,t,"");if(n)for(t in n)r&&n[t]===r[t]||_n(e.style,t,n[t])}else if("o"===t[0]&&"n"===t[1])i=t!==(t=t.replace(/Capture$/,"")),t=t.toLowerCase()in e?t.toLowerCase().slice(2):t.slice(2),e.l||(e.l={}),e.l[t+i]=n,n?r||e.addEventListener(t,i?Pn:jn,i):e.removeEventListener(t,i?Pn:jn,i);else if("dangerouslySetInnerHTML"!==t){if(o)t=t.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if("width"!==t&&"height"!==t&&"href"!==t&&"list"!==t&&"form"!==t&&"tabIndex"!==t&&"download"!==t&&t in e)try{e[t]=null==n?"":n;break e}catch(e){}"function"==typeof n||(null==n||!1===n&&"-"!==t[4]?e.removeAttribute(t):e.setAttribute(t,n))}}function jn(e){return this.l[e.type+!1](Xt.event?Xt.event(e):e)}function Pn(e){return this.l[e.type+!0](Xt.event?Xt.event(e):e)}function wn(e,t,n,r,o,i,u,a,l){var c,s,f,p,m,v,d,y,b,g,h,O,_,S,j,P=t.type;if(void 0!==t.constructor)return null;null!=n.__h&&(l=n.__h,a=t.__e=n.__e,t.__h=null,i=[a]),(c=Xt.__b)&&c(t);try{e:if("function"==typeof P){if(y=t.props,b=(c=P.contextType)&&r[c.__c],g=c?b?b.props.value:c.__:r,n.__c?d=(s=t.__c=n.__c).__=s.__E:("prototype"in P&&P.prototype.render?t.__c=s=new P(y,g):(t.__c=s=new pn(y,g),s.constructor=P,s.render=Cn),b&&b.sub(s),s.props=y,s.state||(s.state={}),s.context=g,s.__n=r,f=s.__d=!0,s.__h=[],s._sb=[]),null==s.__s&&(s.__s=s.state),null!=P.getDerivedStateFromProps&&(s.__s==s.state&&(s.__s=an({},s.__s)),an(s.__s,P.getDerivedStateFromProps(y,s.__s))),p=s.props,m=s.state,s.__v=t,f)null==P.getDerivedStateFromProps&&null!=s.componentWillMount&&s.componentWillMount(),null!=s.componentDidMount&&s.__h.push(s.componentDidMount);else{if(null==P.getDerivedStateFromProps&&y!==p&&null!=s.componentWillReceiveProps&&s.componentWillReceiveProps(y,g),!s.__e&&null!=s.shouldComponentUpdate&&!1===s.shouldComponentUpdate(y,s.__s,g)||t.__v===n.__v){for(t.__v!==n.__v&&(s.props=y,s.state=s.__s,s.__d=!1),s.__e=!1,t.__e=n.__e,t.__k=n.__k,t.__k.forEach((function(e){e&&(e.__=t)})),h=0;h0&&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(xn);r.add({value:t[0],isHighlighted:!0}),""!==t[1]&&r.add({value:t[1],isHighlighted:!1})})),r.get()}function Tn(e){return function(e){if(Array.isArray(e))return qn(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 qn(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 qn(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 qn(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n",""":'"',"'":"'"},Fn=new RegExp(/\w/i),Ln=/&(amp|quot|lt|gt|#39);/g,Un=RegExp(Ln.source);function Mn(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 Fn.test((o=i.value)&&Un.test(o)?o.replace(Ln,(function(e){return Rn[e]})):o)||a!==u?i.isHighlighted:a}function Hn(e){return Hn="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Hn(e)}function Vn(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 Wn(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 ur(e){return function(e){if(Array.isArray(e))return ar(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 ar(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 ar(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 ar(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(y.current||O.value.renderer.renderNoResults);return!n&&r||n},__autocomplete_metadata:{userAgents:br,options:e}}))})),j=f(n({collections:[],completion:null,context:{},isOpen:!1,query:"",activeItemId:null,status:"idle"},O.value.core.initialState)),P={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},w={setActiveItemId:S.value.setActiveItemId,setQuery:S.value.setQuery,setCollections:S.value.setCollections,setIsOpen:S.value.setIsOpen,setStatus:S.value.setStatus,setContext:S.value.setContext,refresh:S.value.refresh,navigator:S.value.navigator},I=m((function(){return Ct.bind(O.value.renderer.renderer.createElement)})),A=m((function(){return Gt({autocomplete:S.value,autocompleteScopeApi:w,classNames:O.value.renderer.classNames,environment:O.value.core.environment,isDetached:_.value,placeholder:O.value.core.placeholder,propGetters:P,setIsModalOpen:k,state:j.current,translations:O.value.renderer.translations})}));function E(){Ht(A.value.panel,{style:_.value?{}:yr({panelPlacement:O.value.renderer.panelPlacement,container:A.value.root,form:A.value.form,environment:O.value.core.environment})})}function D(e){j.current=e;var t={autocomplete:S.value,autocompleteScopeApi:w,classNames:O.value.renderer.classNames,components:O.value.renderer.components,container:O.value.renderer.container,html:I.value,dom:A.value,panelContainer:_.value?A.value.detachedContainer:O.value.renderer.panelContainer,propGetters:P,state:j.current,renderer:O.value.renderer.renderer},r=!b(e)&&!y.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;Vt(o.root,i.getRootProps(n({state:u,props:t.getRootProps({})},r))),Vt(o.input,i.getInputProps(n({state:u,props:t.getInputProps({inputElement:o.input}),inputElement:o.input},r))),Ht(o.label,{hidden:"stalled"===u.status}),Ht(o.loadingIndicator,{hidden:"stalled"!==u.status}),Ht(o.clearButton,{hidden:!u.query}),Ht(o.detachedSearchButtonQuery,{textContent:u.query}),Ht(o.detachedSearchButtonPlaceholder,{hidden:Boolean(u.query)})}(t),function(e,t){var r=t.autocomplete,o=t.autocompleteScopeApi,u=t.classNames,a=t.html,l=t.dom,c=t.panelContainer,s=t.propGetters,f=t.state,p=t.components,m=t.renderer;if(f.isOpen){c.contains(l.panel)||"loading"===f.status||c.appendChild(l.panel),l.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 l=e.source,c=e.items;return m.createElement("section",{key:t,className:u.source,"data-autocomplete-source-id":l.sourceId},l.templates.header&&m.createElement("div",{className:u.sourceHeader},l.templates.header({components:p,createElement:m.createElement,Fragment:m.Fragment,items:c,source:l,state:f,html:a})),l.templates.noResults&&0===c.length?m.createElement("div",{className:u.sourceNoResults},l.templates.noResults({components:p,createElement:m.createElement,Fragment:m.Fragment,source:l,state:f,html:a})):m.createElement("ul",i({className:u.list},s.getListProps(n({state:f,props:r.getListProps({source:l})},o))),c.map((function(e){var t=r.getItemProps({item:e,source:l});return m.createElement("li",i({key:t.id,className:u.item},s.getItemProps(n({state:f,props:t},o))),l.templates.item({components:p,createElement:m.createElement,Fragment:m.Fragment,item:e,state:f,html:a}))}))),l.templates.footer&&m.createElement("div",{className:u.sourceFooter},l.templates.footer({components:p,createElement:m.createElement,Fragment:m.Fragment,items:c,source:l,state:f,html:a})))})),d=m.createElement(m.Fragment,null,m.createElement("div",{className:u.panelLayout},v),m.createElement("div",{className:"aa-GradientBottom"})),y=v.reduce((function(e,t){return e[t.props["data-autocomplete-source-id"]]=t,e}),{});e(n(n({children:d,state:f,sections:v,elements:y},m),{},{components:p,html:a},o),l.panel)}else c.contains(l.panel)&&c.removeChild(l.panel)}(r,t)}function C(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};l();var t=O.value.renderer,n=t.components,r=u(t,gr);g.current=qt(r,O.value.core,{components:Bt(n,(function(e){return!e.value.hasOwnProperty("__autocomplete_componentName")})),initialState:j.current},e),v(),c(),S.value.refresh().then((function(){D(j.current)}))}function k(e){requestAnimationFrame((function(){var t=O.value.core.environment.document.body.contains(A.value.detachedOverlay);e!==t&&(e?(O.value.core.environment.document.body.appendChild(A.value.detachedOverlay),O.value.core.environment.document.body.classList.add("aa-Detached"),A.value.input.focus()):(O.value.core.environment.document.body.removeChild(A.value.detachedOverlay),O.value.core.environment.document.body.classList.remove("aa-Detached")))}))}return a((function(){var e=S.value.getEnvironmentProps({formElement:A.value.form,panelElement:A.value.panel,inputElement:A.value.input});return Ht(O.value.core.environment,e),function(){Ht(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?A.value.detachedOverlay:A.value.panel;return _.value&&j.current.isOpen&&k(!0),D(j.current),function(){e.contains(t)&&e.removeChild(t)}})),a((function(){var e=O.value.renderer.container;return e.appendChild(A.value.root),function(){e.removeChild(A.value.root)}})),a((function(){var e=p((function(e){D(e.state)}),0);return h.current=function(t){var n=t.state,r=t.prevState;(_.value&&r.isOpen!==n.isOpen&&k(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(){h.current=void 0}})),a((function(){var e=p((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){A.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({},w),{},{update:C,destroy:function(){l()}})},e.getAlgoliaFacets=function(e){var t=hr({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=Or,Object.defineProperty(e,"__esModule",{value:!0})})); + diff --git a/_site/site_libs/quarto-search/fuse.min.js b/_site/site_libs/quarto-search/fuse.min.js new file mode 100644 index 0000000..adc2835 --- /dev/null +++ b/_site/site_libs/quarto-search/fuse.min.js @@ -0,0 +1,9 @@ +/** + * Fuse.js v6.6.2 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2022 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 R(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,l=void 0===h?I.ignoreLocation:h,f=r/e.length;if(l)return f;var d=Math.abs(a-o);return u?f+d/u:d?1:f}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,l=i.includeMatches,f=void 0===l?I.includeMatches:l,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:f,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,l=void 0===h?I.findAllMatches:h,f=r.minMatchCharLength,d=void 0===f?I.minMatchCharLength:f,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=R(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,F&&(K[q]|=(A[q+1]|A[q])<<1|1|A[q+1]),K[q]&$&&(C=R(t,{errors:F,currentLocation:B,expectedLocation:b,distance:a,ignoreLocation:p}))<=x){if(x=C,(L=B)<=b)break;z=Math.max(1,2*b-L)}}if(R(t,{errors:F+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:l}),p=y.isMatch,m=y.score,k=y.indices;p&&(g=!0),v+=m,p&&k&&(d=[].concat(f(d),f(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=l(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=l(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=l(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=l(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=l(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=l(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=l(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,l=void 0===h?I.distance:h,f=o.includeMatches,d=void 0===f?I.includeMatches:f,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:l,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=l(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,l=void 0===h?I.ignoreLocation:h,f=n.findAllMatches,d=void 0===f?I.findAllMatches:f,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:l,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||F(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 fe(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(le(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=le(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/site_libs/quarto-search/quarto-search.js b/_site/site_libs/quarto-search/quarto-search.js new file mode 100644 index 0000000..aefb6a5 --- /dev/null +++ b/_site/site_libs/quarto-search/quarto-search.js @@ -0,0 +1,1247 @@ +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 && mainEl) { + // perform any highlighting + highlight(escapeRegExp(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 && 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" : "(max-width: 991px)"; + + // If configured, include the analytics client to send insights + const plugins = configurePlugins(quartoSearchOptions); + + let lastState = null; + const { setIsOpen, setQuery, setCollections } = autocomplete({ + container: searchEl, + detachedMediaQuery: detachedMediaQuery, + defaultActiveItemId: 0, + panelContainer: "#quarto-search-results", + panelPlacement: quartoSearchOptions["panel-placement"], + debug: false, + openOnFocus: true, + plugins, + classNames: { + form: "d-flex", + }, + placeholder: language["search-text-placeholder"], + 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 }) { + // If this is a file URL, note that + + // 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 }) { + if (query === null || query === "") { + return []; + } + + 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 }) { + const hasQuery = lastState.query; + + return createElement( + "div", + { + class: `quarto-search-no-results${ + hasQuery ? "" : " no-query" + }`, + }, + 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, + quartoSearchOptions + ); + }, + }, + }, + ]; + }, + }); + + window.quartoOpenSearch = () => { + setIsOpen(false); + setIsOpen(true); + focusSearchInput(); + }; + + document.addEventListener("keyup", (event) => { + const { key } = event; + const kbds = quartoSearchOptions["keyboard-shortcut"]; + const focusedEl = document.activeElement; + + const isFormElFocused = [ + "input", + "select", + "textarea", + "button", + "option", + ].find((tag) => { + return focusedEl.tagName.toLowerCase() === tag; + }); + + if ( + kbds && + kbds.includes(key) && + !isFormElFocused && + !document.activeElement.isContentEditable + ) { + event.preventDefault(); + window.quartoOpenSearch(); + } + }); + + // 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"); + } + } + + function throttle(func, wait) { + let waiting = false; + return function () { + if (!waiting) { + func.apply(this, arguments); + waiting = true; + setTimeout(function () { + waiting = false; + }, wait); + } + }; + } + + // 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 = throttle(() => { + // Only do this if we're not detached + // Bug #7117 + // This will happen when the keyboard is shown on ios (resulting in a scroll) + // which then closed the search UI + if (!window.matchMedia(detachedMediaQuery).matches) { + setIsOpen(false); + } + }, 50); + + 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.flatMap((event) => { + // This API limits the number of items per event to 20 + const chunkSize = 20; + const itemChunks = []; + const eventItems = event.items; + for (let i = 0; i < eventItems.length; i += chunkSize) { + itemChunks.push(eventItems.slice(i, i + chunkSize)); + } + // Split the items into multiple events that can be sent + const events = itemChunks.map((items) => { + return { + ...event, + items, + }; + }); + return events; + }); + + for (const event of events) { + insights.viewedObjectIDs(event); + } + }, + }); + 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; +var shownWarning = false; +async function readSearchData() { + // Initialize the search index on demand + if (fuseIndex === undefined) { + if (window.location.protocol === "file:" && !shownWarning) { + window.alert( + "Search requires JavaScript features disabled when running in file://... URLs. In order to use search, please run this document in a web server." + ); + shownWarning = true; + return; + } + // 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, + quartoSearchOptions +) { + switch (item.type) { + case kItemTypeDoc: + return createDocumentCard( + createElement, + "file-richtext", + item.title, + item.section, + item.text, + item.href, + item.crumbs, + quartoSearchOptions + ); + 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, + crumbs, + quartoSearchOptions +) { + const iconEl = createElement("i", { + class: `bi bi-${icon} search-result-icon`, + }); + const titleEl = createElement("p", { class: "search-result-title" }, title); + const titleContents = [iconEl, titleEl]; + const showParent = quartoSearchOptions["show-item-context"]; + if (crumbs && showParent) { + let crumbsOut = undefined; + const crumbClz = ["search-result-crumbs"]; + if (showParent === "root") { + crumbsOut = crumbs.length > 1 ? crumbs[0] : undefined; + } else if (showParent === "parent") { + crumbsOut = crumbs.length > 1 ? crumbs[crumbs.length - 2] : undefined; + } else { + crumbsOut = crumbs.length > 1 ? crumbs.join(" > ") : undefined; + crumbClz.push("search-result-crumbs-wrap"); + } + + const crumbEl = createElement( + "p", + { class: crumbClz.join(" ") }, + crumbsOut + ); + titleContents.push(crumbEl); + } + + const titleContainerEl = createElement( + "div", + { class: "search-result-title-container" }, + titleContents + ); + + 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); + } + } + } +} + +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string +} + +// 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", "crumbs"].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), + crumbs: result.item.crumbs, + }; + }); +} diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docs/images/learning.gif b/docs/images/learning.gif new file mode 100644 index 0000000..05213a5 Binary files /dev/null and b/docs/images/learning.gif differ diff --git a/docs/images/type-i-and-ii-error-2.png b/docs/images/type-i-and-ii-error-2.png new file mode 100644 index 0000000..cc77f71 Binary files /dev/null and b/docs/images/type-i-and-ii-error-2.png differ diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..42b15cb --- /dev/null +++ b/docs/index.html @@ -0,0 +1,8 @@ + + + Redirect to notebooks/cleaning_data.html + + + + + diff --git a/docs/notebooks/cleaning_data.html b/docs/notebooks/cleaning_data.html new file mode 100644 index 0000000..9fc4dd6 --- /dev/null +++ b/docs/notebooks/cleaning_data.html @@ -0,0 +1,3143 @@ + + + + + + + + + +Cleaning Data + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+

Cleaning Data

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Cleaning Data

+
+

About the data

+

In this notebook, we will using daily temperature data from the National Centers for Environmental Information (NCEI) API. We will use the Global Historical Climatology Network - Daily (GHCND) dataset; see the documentation here.

+

This data was collected from the LaGuardia Airport station in New York City for October 2018. It contains: - the daily minimum temperature (TMIN) - the daily maximum temperature (TMAX) - the daily average temperature (TAVG)

+

Note: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.

+

In addition, we will be using S&P 500 stock market data (obtained using the stock_analysis package and data for bitcoin for 2017 through 2018. For the first edition, the bitcoin data was collected from CoinMarketCap using the stock_analysis package; however, changes in the website led to the necessity of changing the data source to Yahoo! Finance. The bitcoin data that was collected before the CoinMarketCap website change should be equivalent to the historical data that can be viewed on this page.

+
+
+

Setup

+

We need to import pandas and read in the temperature data to get started:

+
+
import pandas as pd
+
+df = pd.read_csv('../data/nyc_temperatures.csv')
+df.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationattributesvalue
02018-10-01T00:00:00TAVGGHCND:USW00014732H,,S,21.2
12018-10-01T00:00:00TMAXGHCND:USW00014732,,W,240025.6
22018-10-01T00:00:00TMINGHCND:USW00014732,,W,240018.3
32018-10-02T00:00:00TAVGGHCND:USW00014732H,,S,22.7
42018-10-02T00:00:00TMAXGHCND:USW00014732,,W,240026.1
+ +
+
+
+
+
+

Renaming Columns

+

We start out with the following columns:

+
+
df.columns
+
+
Index(['date', 'datatype', 'station', 'attributes', 'value'], dtype='object')
+
+
+

We want to rename the value column to indicate it contains the temperature in Celsius and the attributes column to say flags since each value in the comma-delimited string is a different flag about the data collection. For this task, we use the rename() method and pass in a dictionary mapping the column names to their new names. We pass inplace=True to change our original dataframe instead of getting a new one back:

+
+
df.rename(
+    columns={
+        'value': 'temp_C',
+        'attributes': 'flags'
+    }, inplace=True
+)
+
+

Those columns have been successfully renamed:

+
+
df.columns
+
+
Index(['date', 'datatype', 'station', 'flags', 'temp_C'], dtype='object')
+
+
+

We can also perform string operations on the column names with rename():

+
+
df.rename(str.upper, axis='columns').columns
+
+
Index(['DATE', 'DATATYPE', 'STATION', 'FLAGS', 'TEMP_C'], dtype='object')
+
+
+
+
+

Type Conversion

+

The date column is not currently being stored as a datetime:

+
+
df.dtypes
+
+
date         object
+datatype     object
+station      object
+flags        object
+temp_C      float64
+dtype: object
+
+
+

Let’s perform the conversion with pd.to_datetime():

+
+
df.loc[:,'date'] = pd.to_datetime(df.date)
+df.dtypes
+
+
date         object
+datatype     object
+station      object
+flags        object
+temp_C      float64
+dtype: object
+
+
+

Now we get useful information when we use describe() on this column:

+
+
df.date.describe()
+
+
count                      93
+unique                     31
+top       2018-10-01 00:00:00
+freq                        3
+Name: date, dtype: object
+
+
+

We can use tz_localize() on a DatetimeIndex object to convert to a desired timezone:

+
+
pd.date_range(start='2018-10-25', periods=2, freq='D').tz_localize('EST')
+
+
DatetimeIndex(['2018-10-25 00:00:00-05:00', '2018-10-26 00:00:00-05:00'], dtype='datetime64[ns, EST]', freq=None)
+
+
+

This also works with Series/DataFrame objects that have an index of type DatetimeIndex. Let’s read in the CSV again for this example and set the date column to be the index and stored as a datetime:

+
+
eastern = pd.read_csv(
+    '../data/nyc_temperatures.csv', index_col='date', parse_dates=True
+).tz_localize('EST')
+eastern.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datatypestationattributesvalue
date
2018-10-01 00:00:00-05:00TAVGGHCND:USW00014732H,,S,21.2
2018-10-01 00:00:00-05:00TMAXGHCND:USW00014732,,W,240025.6
2018-10-01 00:00:00-05:00TMINGHCND:USW00014732,,W,240018.3
2018-10-02 00:00:00-05:00TAVGGHCND:USW00014732H,,S,22.7
2018-10-02 00:00:00-05:00TMAXGHCND:USW00014732,,W,240026.1
+ +
+
+
+

We can use tz_convert() to convert to another timezone from there. If we convert the Eastern datetimes to UTC, they will now be at 5 AM, since pandas will use the offsets to convert:

+
+
eastern.tz_convert('UTC').head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datatypestationattributesvalue
date
2018-10-01 05:00:00+00:00TAVGGHCND:USW00014732H,,S,21.2
2018-10-01 05:00:00+00:00TMAXGHCND:USW00014732,,W,240025.6
2018-10-01 05:00:00+00:00TMINGHCND:USW00014732,,W,240018.3
2018-10-02 05:00:00+00:00TAVGGHCND:USW00014732H,,S,22.7
2018-10-02 05:00:00+00:00TMAXGHCND:USW00014732,,W,240026.1
+ +
+
+
+

We can change the period of the index as well. We could change the period to be monthly to make it easier to aggregate later.

+

The reason we have to add the parameter within tz_localize() to None for this, is because we’ll get a warning from pandas that our output class PeriodArray doesn’t have time zone information and we’ll lose it.

+
+
eastern.tz_localize(None).to_period('M').index
+
+
PeriodIndex(['2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',
+             '2018-10', '2018-10', '2018-10'],
+            dtype='period[M]', name='date')
+
+
+

We now get a PeriodIndex object, which we can change back into a DatetimeIndex object with to_timestamp():

+
+
eastern.tz_localize(None).to_period('M').to_timestamp().index
+
+
DatetimeIndex(['2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',
+               '2018-10-01'],
+              dtype='datetime64[ns]', name='date', freq=None)
+
+
+

We can use the assign() method for working with multiple columns at once (or creating new ones). Since our date column has already been converted, we need to read in the data again:

+
+
df = pd.read_csv('../data/nyc_temperatures.csv').rename(
+    columns={
+        'value': 'temp_C',
+        'attributes': 'flags'
+    }
+)
+
+new_df = df.assign(
+    date=pd.to_datetime(df.date),
+    temp_F=(df.temp_C * 9/5) + 32
+)
+new_df.dtypes
+
+
date        datetime64[ns]
+datatype            object
+station             object
+flags               object
+temp_C             float64
+temp_F             float64
+dtype: object
+
+
+

The date column now has datetimes and the temp_F column was added:

+
+
new_df.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_F
02018-10-01TAVGGHCND:USW00014732H,,S,21.270.16
12018-10-01TMAXGHCND:USW00014732,,W,240025.678.08
22018-10-01TMINGHCND:USW00014732,,W,240018.364.94
32018-10-02TAVGGHCND:USW00014732H,,S,22.772.86
42018-10-02TMAXGHCND:USW00014732,,W,240026.178.98
+ +
+
+
+

We can also use astype() to perform conversions. Let’s create columns of the integer portion of the temperatures in Celsius and Fahrenheit. We will use lambda functions (first introduced in Chapter 2, Working with Pandas DataFrames), so that we can use the values being created in the temp_F column to calculate the temp_F_whole column. It is very common (and useful) to use lambda functions with assign():

+
+
df = df.assign(
+    date=lambda x: pd.to_datetime(x.date),
+    temp_C_whole=lambda x: x.temp_C.astype('int'),
+    temp_F=lambda x: (x.temp_C * 9/5) + 32,
+    temp_F_whole=lambda x: x.temp_F.astype('int')
+)
+
+df.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
02018-10-01TAVGGHCND:USW00014732H,,S,21.22170.1670
12018-10-01TMAXGHCND:USW00014732,,W,240025.62578.0878
22018-10-01TMINGHCND:USW00014732,,W,240018.31864.9464
32018-10-02TAVGGHCND:USW00014732H,,S,22.72272.8672
42018-10-02TMAXGHCND:USW00014732,,W,240026.12678.9878
+ +
+
+
+

Creating categories:

+
+
df_with_categories = df.assign(
+    station=df.station.astype('category'),
+    datatype=df.datatype.astype('category')
+)
+df_with_categories.dtypes
+
+
date            datetime64[ns]
+datatype              category
+station               category
+flags                   object
+temp_C                 float64
+temp_C_whole             int32
+temp_F                 float64
+temp_F_whole             int32
+dtype: object
+
+
+
+
df_with_categories.describe(include='category')
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datatypestation
count9393
unique31
topTAVGGHCND:USW00014732
freq3193
+ +
+
+
+

Our categories have no order, but this is something that pandas supports:

+
+
pd.Categorical(
+    ['med', 'med', 'low', 'high'], 
+    categories=['low', 'med', 'high'],
+    ordered=True
+)
+
+
['med', 'med', 'low', 'high']
+Categories (3, object): ['low' < 'med' < 'high']
+
+
+
+
+

Reordering, reindexing, and sorting

+

Say we want to find the days that reached the hottest temperatures in the weather data; we can sort our values by the temp_C column with the largest on top to find this:

+
+
df[df.datatype == 'TMAX'].sort_values(by='temp_C', ascending=False).head(10)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
192018-10-07TMAXGHCND:USW00014732,,W,240027.82782.0482
282018-10-10TMAXGHCND:USW00014732,,W,240027.82782.0482
312018-10-11TMAXGHCND:USW00014732,,W,240026.72680.0680
102018-10-04TMAXGHCND:USW00014732,,W,240026.12678.9878
42018-10-02TMAXGHCND:USW00014732,,W,240026.12678.9878
12018-10-01TMAXGHCND:USW00014732,,W,240025.62578.0878
252018-10-09TMAXGHCND:USW00014732,,W,240025.62578.0878
72018-10-03TMAXGHCND:USW00014732,,W,240025.02577.0077
132018-10-05TMAXGHCND:USW00014732,,W,240022.82273.0473
222018-10-08TMAXGHCND:USW00014732,,W,240022.82273.0473
+ +
+
+
+

However, this isn’t perfect because we have some ties, and they aren’t sorted consistently. In the first tie between the 7th and the 10th, the earlier date comes first, but the opposite is true with the tie between the 4th and the 2nd. We can use other columns to break ties and specify how to sort each with ascending. Let’s break ties with the date column and show earlier dates before later ones:

+
+
df[df.datatype == 'TMAX'].sort_values(by=['temp_C', 'date'], ascending=[False, True]).head(10)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
192018-10-07TMAXGHCND:USW00014732,,W,240027.82782.0482
282018-10-10TMAXGHCND:USW00014732,,W,240027.82782.0482
312018-10-11TMAXGHCND:USW00014732,,W,240026.72680.0680
42018-10-02TMAXGHCND:USW00014732,,W,240026.12678.9878
102018-10-04TMAXGHCND:USW00014732,,W,240026.12678.9878
12018-10-01TMAXGHCND:USW00014732,,W,240025.62578.0878
252018-10-09TMAXGHCND:USW00014732,,W,240025.62578.0878
72018-10-03TMAXGHCND:USW00014732,,W,240025.02577.0077
132018-10-05TMAXGHCND:USW00014732,,W,240022.82273.0473
222018-10-08TMAXGHCND:USW00014732,,W,240022.82273.0473
+ +
+
+
+

Notice that the index was jumbled in the past 2 results. Here, our index only stores the row number in the original data, but we may not need to keep track of that information. In this case, we can pass in ignore_index=True to get a new index after sorting:

+
+
df[df.datatype == 'TMAX'].sort_values(by=['temp_C', 'date'], ascending=[False, True], ignore_index=True).head(10)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
02018-10-07TMAXGHCND:USW00014732,,W,240027.82782.0482
12018-10-10TMAXGHCND:USW00014732,,W,240027.82782.0482
22018-10-11TMAXGHCND:USW00014732,,W,240026.72680.0680
32018-10-02TMAXGHCND:USW00014732,,W,240026.12678.9878
42018-10-04TMAXGHCND:USW00014732,,W,240026.12678.9878
52018-10-01TMAXGHCND:USW00014732,,W,240025.62578.0878
62018-10-09TMAXGHCND:USW00014732,,W,240025.62578.0878
72018-10-03TMAXGHCND:USW00014732,,W,240025.02577.0077
82018-10-05TMAXGHCND:USW00014732,,W,240022.82273.0473
92018-10-08TMAXGHCND:USW00014732,,W,240022.82273.0473
+ +
+
+
+

When just looking for the n-largest values, rather than wanting to sort all the data, we can use nlargest():

+
+
df[df.datatype == 'TAVG'].nlargest(n=10, columns='temp_C')
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
272018-10-10TAVGGHCND:USW00014732H,,S,23.82374.8474
302018-10-11TAVGGHCND:USW00014732H,,S,23.42374.1274
182018-10-07TAVGGHCND:USW00014732H,,S,22.82273.0473
32018-10-02TAVGGHCND:USW00014732H,,S,22.72272.8672
62018-10-03TAVGGHCND:USW00014732H,,S,21.82171.2471
242018-10-09TAVGGHCND:USW00014732H,,S,21.82171.2471
92018-10-04TAVGGHCND:USW00014732H,,S,21.32170.3470
02018-10-01TAVGGHCND:USW00014732H,,S,21.22170.1670
212018-10-08TAVGGHCND:USW00014732H,,S,20.92069.6269
122018-10-05TAVGGHCND:USW00014732H,,S,20.32068.5468
+ +
+
+
+

We use nsmallest() for the n-smallest values.

+
+
df.nsmallest(n=5, columns=['temp_C', 'date'])
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
652018-10-22TMINGHCND:USW00014732,,W,24005.6542.0842
772018-10-26TMINGHCND:USW00014732,,W,24005.6542.0842
622018-10-21TMINGHCND:USW00014732,,W,24006.1642.9842
742018-10-25TMINGHCND:USW00014732,,W,24006.1642.9842
532018-10-18TMINGHCND:USW00014732,,W,24006.7644.0644
+ +
+
+
+

The sample() method will give us rows (or columns with axis=1) at random. We can provide a seed (random_state) to make this reproducible. The index after we do this is jumbled:

+
+
df.sample(5, random_state=0).index
+
+
Index([2, 30, 55, 16, 13], dtype='int64')
+
+
+

We can use sort_index() to order it again:

+
+
df.sample(5, random_state=0).sort_index().index
+
+
Index([2, 13, 16, 30, 55], dtype='int64')
+
+
+

The sort_index() method can also sort columns alphabetically:

+
+
df.sort_index(axis=1).head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datatypedateflagsstationtemp_Ctemp_C_wholetemp_Ftemp_F_whole
0TAVG2018-10-01H,,S,GHCND:USW0001473221.22170.1670
1TMAX2018-10-01,,W,2400GHCND:USW0001473225.62578.0878
2TMIN2018-10-01,,W,2400GHCND:USW0001473218.31864.9464
3TAVG2018-10-02H,,S,GHCND:USW0001473222.72272.8672
4TMAX2018-10-02,,W,2400GHCND:USW0001473226.12678.9878
+ +
+
+
+

This can make selection with loc easier for many columns:

+
+
df.sort_index(axis=1).head().loc[:,'temp_C':'temp_F_whole']
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
temp_Ctemp_C_wholetemp_Ftemp_F_whole
021.22170.1670
125.62578.0878
218.31864.9464
322.72272.8672
426.12678.9878
+ +
+
+
+

We must sort the index to compare two dataframes. If the index is different, but the data is the same, they will be marked not-equal:

+
+
df.equals(df.sort_values(by='temp_C'))
+
+
False
+
+
+

Sorting the index solves this issue:

+
+
df.equals(df.sort_values(by='temp_C').sort_index())
+
+
True
+
+
+

Let’s set the date column as our index:

+
+
df.set_index('date', inplace=True)
+df.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
date
2018-10-01TAVGGHCND:USW00014732H,,S,21.22170.1670
2018-10-01TMAXGHCND:USW00014732,,W,240025.62578.0878
2018-10-01TMINGHCND:USW00014732,,W,240018.31864.9464
2018-10-02TAVGGHCND:USW00014732H,,S,22.72272.8672
2018-10-02TMAXGHCND:USW00014732,,W,240026.12678.9878
+ +
+
+
+

Now that we have an index of type DatetimeIndex, we can do datetime slicing and indexing. As long as we provide a date format that pandas understands, we can grab the data. To select all of 2018, we simply use df.loc['2018'], for the fourth quarter of 2018 we can use df.loc['2018-Q4'], grabbing October is as simple as using df.loc['2018-10']; these can also be combined to build ranges. Let’s grab October 11, 2018 through October 12, 2018 (inclusive of both endpoints)—note that using loc[] is optional for ranges:

+
+
df['2018-10-11':'2018-10-12']
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
date
2018-10-11TAVGGHCND:USW00014732H,,S,23.42374.1274
2018-10-11TMAXGHCND:USW00014732,,W,240026.72680.0680
2018-10-11TMINGHCND:USW00014732,,W,240021.72171.0671
2018-10-12TAVGGHCND:USW00014732H,,S,18.31864.9464
2018-10-12TMAXGHCND:USW00014732,,W,240022.22271.9671
2018-10-12TMINGHCND:USW00014732,,W,240012.21253.9653
+ +
+
+
+

We can also use reset_index() to get a fresh index and move our current index into a column for safe keeping. This is especially useful if we had data, such as the date, in the index that we don’t want to lose:

+
+
df['2018-10-11':'2018-10-12'].reset_index()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypestationflagstemp_Ctemp_C_wholetemp_Ftemp_F_whole
02018-10-11TAVGGHCND:USW00014732H,,S,23.42374.1274
12018-10-11TMAXGHCND:USW00014732,,W,240026.72680.0680
22018-10-11TMINGHCND:USW00014732,,W,240021.72171.0671
32018-10-12TAVGGHCND:USW00014732H,,S,18.31864.9464
42018-10-12TMAXGHCND:USW00014732,,W,240022.22271.9671
52018-10-12TMINGHCND:USW00014732,,W,240012.21253.9653
+ +
+
+
+

Reindexing allows us to conform our axis to contain a given set of labels. Let’s turn to the S&P 500 stock data in the sp500.csv file to see an example of this. Notice we only have data for trading days (weekdays, excluding holidays):

+
+
sp = pd.read_csv(
+    '../data/sp500.csv', index_col='date', parse_dates=True
+).drop(columns=['adj_close'])
+
+sp.head(10).assign(
+    day_of_week=lambda x: x.index.day_name()
+)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
highlowopenclosevolumeday_of_week
date
2017-01-032263.8798832245.1298832251.5700682257.8300783770530000Tuesday
2017-01-042272.8200682261.6000982261.6000982270.7500003764890000Wednesday
2017-01-052271.5000002260.4499512268.1799322269.0000003761820000Thursday
2017-01-062282.1000982264.0600592271.1398932276.9799803339890000Friday
2017-01-092275.4899902268.8999022273.5900882268.8999023217610000Monday
2017-01-102279.2700202265.2700202269.7199712268.8999023638790000Tuesday
2017-01-112275.3200682260.8300782268.6000982275.3200683620410000Wednesday
2017-01-122271.7800292254.2500002271.1398932270.4399413462130000Thursday
2017-01-132278.6799322271.5100102272.7399902274.6398933081270000Friday
2017-01-172272.0800782262.8100592269.1398932267.8898933584990000Tuesday
+ +
+
+
+

If we want to look at the value of a portfolio (group of assets) that trade on different days, we need to handle the mismatch in the index. Bitcoin, for example, trades daily. If we sum up all the data we have for each day (aggregations will be covered in chapter 4, so don’t fixate on this part), we get the following:

+
+
bitcoin = pd.read_csv(
+    '../data/bitcoin.csv', index_col='date', parse_dates=True
+).drop(columns=['market_cap'])
+
+# every day's closing price = S&P 500 close + Bitcoin close (same for other metrics)
+portfolio = pd.concat([sp, bitcoin], sort=False).groupby(level='date').sum()
+
+portfolio.head(10).assign(
+    day_of_week=lambda x: x.index.day_name()
+)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
highlowopenclosevolumeday_of_week
date
2017-01-011003.080000958.700000963.660000998.330000147775008Sunday
2017-01-021031.390000996.700000998.6200001021.750000222184992Monday
2017-01-033307.9598833266.7298833273.1700683301.6700783955698000Tuesday
2017-01-043432.2400683306.0000983306.0000983425.4800004109835984Wednesday
2017-01-053462.6000003170.8699513424.9099323282.3800004272019008Thursday
2017-01-063328.9100983148.0000593285.3798933179.1799803691766000Friday
2017-01-07908.590000823.560000903.490000908.590000279550016Saturday
2017-01-08942.720000887.250000908.170000911.200000158715008Sunday
2017-01-093189.1799903148.7099023186.8300883171.7299023359486992Monday
2017-01-103194.1400203166.3300203172.1599713176.5799023754598000Tuesday
+ +
+
+
+

It may not be immediately obvious what is wrong with the previous data, but with a visualization we can easily see the cyclical pattern of drops on the days the stock market is closed. (Don’t worry about the plotting code too much, we will cover it in depth in chapters 5 and 6).

+

We will need to import matplotlib now:

+
+
import matplotlib.pyplot as plt # we use this module for plotting
+from matplotlib.ticker import StrMethodFormatter # for formatting the axis
+
+

Now we can see why we need to reindex:

+
+
# plot the closing price from Q4 2017 through Q2 2018
+ax = portfolio['2017-Q4':'2018-Q2'].plot(
+    y='close', figsize=(15, 5), legend=False,
+    title='Bitcoin + S&P 500 value without accounting for different indices'
+)
+
+# formatting
+ax.set_ylabel('price')
+ax.yaxis.set_major_formatter(StrMethodFormatter('${x:,.0f}'))
+for spine in ['top', 'right']:
+    ax.spines[spine].set_visible(False)
+
+# show the plot
+plt.show()
+
+
+
+

+
+
+
+
+

We need to align the index of the S&P 500 to match bitcoin in order to fix this. We will use the reindex() method, but by default we get NaN for the values that we don’t have data for:

+
+
sp.reindex(bitcoin.index).head(10).assign(
+    day_of_week=lambda x: x.index.day_name()
+)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
highlowopenclosevolumeday_of_week
date
2017-01-01NaNNaNNaNNaNNaNSunday
2017-01-02NaNNaNNaNNaNNaNMonday
2017-01-032263.8798832245.1298832251.5700682257.8300783.770530e+09Tuesday
2017-01-042272.8200682261.6000982261.6000982270.7500003.764890e+09Wednesday
2017-01-052271.5000002260.4499512268.1799322269.0000003.761820e+09Thursday
2017-01-062282.1000982264.0600592271.1398932276.9799803.339890e+09Friday
2017-01-07NaNNaNNaNNaNNaNSaturday
2017-01-08NaNNaNNaNNaNNaNSunday
2017-01-092275.4899902268.8999022273.5900882268.8999023.217610e+09Monday
2017-01-102279.2700202265.2700202269.7199712268.8999023.638790e+09Tuesday
+ +
+
+
+

So now we have rows for every day of the year, but all the weekends and holidays have NaN values. To address this, we can specify how to handle missing values with the method argument. In this case, we want to forward-fill, which will put the weekend and holiday values as the value they had for the Friday (or end of trading week) before:

+
+
sp.reindex(bitcoin.index, method='ffill').head(10)\
+    .assign(day_of_week=lambda x: x.index.day_name())
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
highlowopenclosevolumeday_of_week
date
2017-01-01NaNNaNNaNNaNNaNSunday
2017-01-02NaNNaNNaNNaNNaNMonday
2017-01-032263.8798832245.1298832251.5700682257.8300783.770530e+09Tuesday
2017-01-042272.8200682261.6000982261.6000982270.7500003.764890e+09Wednesday
2017-01-052271.5000002260.4499512268.1799322269.0000003.761820e+09Thursday
2017-01-062282.1000982264.0600592271.1398932276.9799803.339890e+09Friday
2017-01-072282.1000982264.0600592271.1398932276.9799803.339890e+09Saturday
2017-01-082282.1000982264.0600592271.1398932276.9799803.339890e+09Sunday
2017-01-092275.4899902268.8999022273.5900882268.8999023.217610e+09Monday
2017-01-102279.2700202265.2700202269.7199712268.8999023.638790e+09Tuesday
+ +
+
+
+

To isolate the changes happening with the forward-filling, we can use the compare() method. It shows us the values that differ across identically-labeled dataframes (same names and same columns). Here, we can see that only weekends and holidays (Monday, January 16, 2017 was MLK day) have values forward-filled. Notice that consecutive days have the same values.

+
+
sp.reindex(bitcoin.index)\
+    .compare(sp.reindex(bitcoin.index, method='ffill'))\
+    .head(10).assign(day_of_week=lambda x: x.index.day_name())
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
highlowopenclosevolumeday_of_week
selfotherselfotherselfotherselfotherselfother
date
2017-01-07NaN2282.100098NaN2264.060059NaN2271.139893NaN2276.979980NaN3.339890e+09Saturday
2017-01-08NaN2282.100098NaN2264.060059NaN2271.139893NaN2276.979980NaN3.339890e+09Sunday
2017-01-14NaN2278.679932NaN2271.510010NaN2272.739990NaN2274.639893NaN3.081270e+09Saturday
2017-01-15NaN2278.679932NaN2271.510010NaN2272.739990NaN2274.639893NaN3.081270e+09Sunday
2017-01-16NaN2278.679932NaN2271.510010NaN2272.739990NaN2274.639893NaN3.081270e+09Monday
2017-01-21NaN2276.959961NaN2265.010010NaN2269.959961NaN2271.310059NaN3.524970e+09Saturday
2017-01-22NaN2276.959961NaN2265.010010NaN2269.959961NaN2271.310059NaN3.524970e+09Sunday
2017-01-28NaN2299.020020NaN2291.620117NaN2299.020020NaN2294.689941NaN3.135890e+09Saturday
2017-01-29NaN2299.020020NaN2291.620117NaN2299.020020NaN2294.689941NaN3.135890e+09Sunday
2017-02-04NaN2298.310059NaN2287.879883NaN2288.540039NaN2297.419922NaN3.597970e+09Saturday
+ +
+
+
+

This isn’t perfect though. We probably want 0 for the volume traded and to put the closing price for the open, high, low, and close on the days the market is closed:

+

The reason why we’re using np.where(boolean condition, value if True, value if False) within lambda functions in the example below, is that vectorized operations allow us to be faster and more efficient than utilizing for loops to perform calculations on arrays all at once.

+
+
import numpy as np
+
+sp_reindexed = sp.reindex(bitcoin.index).assign(
+    volume=lambda x: x.volume.fillna(0), # put 0 when market is closed
+    close=lambda x: x.close.fillna(method='ffill'), # carry this forward
+    # take the closing price if these aren't available
+    open=lambda x: np.where(x.open.isnull(), x.close, x.open),
+    high=lambda x: np.where(x.high.isnull(), x.close, x.high),
+    low=lambda x: np.where(x.low.isnull(), x.close, x.low)
+)
+sp_reindexed.head(10).assign(
+    day_of_week=lambda x: x.index.day_name()
+)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
highlowopenclosevolumeday_of_week
date
2017-01-01NaNNaNNaNNaN0.000000e+00Sunday
2017-01-02NaNNaNNaNNaN0.000000e+00Monday
2017-01-032263.8798832245.1298832251.5700682257.8300783.770530e+09Tuesday
2017-01-042272.8200682261.6000982261.6000982270.7500003.764890e+09Wednesday
2017-01-052271.5000002260.4499512268.1799322269.0000003.761820e+09Thursday
2017-01-062282.1000982264.0600592271.1398932276.9799803.339890e+09Friday
2017-01-072276.9799802276.9799802276.9799802276.9799800.000000e+00Saturday
2017-01-082276.9799802276.9799802276.9799802276.9799800.000000e+00Sunday
2017-01-092275.4899902268.8999022273.5900882268.8999023.217610e+09Monday
2017-01-102279.2700202265.2700202269.7199712268.8999023.638790e+09Tuesday
+ +
+
+
+

If we create a visualization comparing the reindexed data to the first attempt, we see how reindexing helped maintain the asset value when the market was closed:

+
+
# every day's closing price = S&P 500 close adjusted for market closure + Bitcoin close (same for other metrics)
+fixed_portfolio = sp_reindexed + bitcoin
+
+# plot the reindexed portfolio's closing price from Q4 2017 through Q2 2018
+ax = fixed_portfolio['2017-Q4':'2018-Q2'].plot(
+    y='close', label='reindexed portfolio of S&P 500 + Bitcoin', figsize=(15, 5), linewidth=2, 
+    title='Reindexed portfolio vs. portfolio with mismatched indices'
+)
+
+# add line for original portfolio for comparison
+portfolio['2017-Q4':'2018-Q2'].plot(
+    y='close', ax=ax, linestyle='--', label='portfolio of S&P 500 + Bitcoin w/o reindexing'
+)
+
+# formatting
+ax.set_ylabel('price')
+ax.yaxis.set_major_formatter(StrMethodFormatter('${x:,.0f}'))
+for spine in ['top', 'right']:
+    ax.spines[spine].set_visible(False)
+
+# show the plot
+plt.show()
+
+
+
+

+
+
+
+
+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/docs/notebooks/cleaning_data_files/figure-html/cell-38-output-1.png b/docs/notebooks/cleaning_data_files/figure-html/cell-38-output-1.png new file mode 100644 index 0000000..a2de033 Binary files /dev/null and b/docs/notebooks/cleaning_data_files/figure-html/cell-38-output-1.png differ diff --git a/docs/notebooks/cleaning_data_files/figure-html/cell-43-output-1.png b/docs/notebooks/cleaning_data_files/figure-html/cell-43-output-1.png new file mode 100644 index 0000000..7176ca7 Binary files /dev/null and b/docs/notebooks/cleaning_data_files/figure-html/cell-43-output-1.png differ diff --git a/docs/notebooks/handling_data_issues.html b/docs/notebooks/handling_data_issues.html new file mode 100644 index 0000000..47ac900 --- /dev/null +++ b/docs/notebooks/handling_data_issues.html @@ -0,0 +1,2214 @@ + + + + + + + + + +Handling duplicate, missing, or invalid data + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+

Handling duplicate, missing, or invalid data

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Handling duplicate, missing, or invalid data

+
+

About the data

+

In this notebook, we will using daily weather data that was taken from the National Centers for Environmental Information (NCEI) API and altered to introduce many common problems faced when working with data.

+

Note: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.

+
+
+

Background on the data

+

Data meanings: - PRCP: precipitation in millimeters - SNOW: snowfall in millimeters - SNWD: snow depth in millimeters - TMAX: maximum daily temperature in Celsius - TMIN: minimum daily temperature in Celsius - TOBS: temperature at time of observation in Celsius - WESF: water equivalent of snow in millimeters

+

Some important facts to get our bearings: - According to the National Weather Service, the coldest temperature ever recorded in Central Park was -15°F (-26.1°C) on February 9, 1934: source - The temperature of the Sun’s photosphere is approximately 5,505°C: source

+
+
+

Setup

+

We need to import pandas and read in the dirty data to get started:

+
+
import pandas as pd
+
+df = pd.read_csv('../data/dirty_data.csv')
+
+
+
+

Finding problematic data

+

A good first step is to look at some rows:

+
+
df.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datestationPRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
02018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
12018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
22018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
32018-01-02T00:00:00GHCND:USC002809070.00.0-inf-8.3-16.1-12.2NaNFalse
42018-01-03T00:00:00GHCND:USC002809070.00.0-inf-4.4-13.9-13.3NaNFalse
+ +
+
+
+

Looking at summary statistics can reveal strange or missing values:

+
+
df.describe()
+
+
c:\Users\gpower\AppData\Local\mambaforge\envs\cary_dev\Lib\site-packages\numpy\lib\function_base.py:4573: RuntimeWarning: invalid value encountered in subtract
+  diff_b_a = subtract(b, a)
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESF
count765.000000577.000000577.0765.000000765.000000398.00000011.000000
mean5.3603924.202773NaN2649.175294-15.9143798.63216116.290909
std10.00213825.086077NaN2744.15628124.2428499.8150549.489832
min0.0000000.000000-inf-11.700000-40.000000-16.1000001.800000
25%0.0000000.000000NaN13.300000-40.0000000.1500008.600000
50%0.0000000.000000NaN32.800000-11.1000008.30000019.300000
75%5.8000000.000000NaN5505.0000006.70000018.30000024.900000
max61.700000229.000000inf5505.00000023.90000026.10000028.700000
+ +
+
+
+

The info() method can pinpoint missing values and wrong data types:

+
+
df.info()
+
+
<class 'pandas.core.frame.DataFrame'>
+RangeIndex: 765 entries, 0 to 764
+Data columns (total 10 columns):
+ #   Column             Non-Null Count  Dtype  
+---  ------             --------------  -----  
+ 0   date               765 non-null    object 
+ 1   station            765 non-null    object 
+ 2   PRCP               765 non-null    float64
+ 3   SNOW               577 non-null    float64
+ 4   SNWD               577 non-null    float64
+ 5   TMAX               765 non-null    float64
+ 6   TMIN               765 non-null    float64
+ 7   TOBS               398 non-null    float64
+ 8   WESF               11 non-null     float64
+ 9   inclement_weather  408 non-null    object 
+dtypes: float64(7), object(3)
+memory usage: 59.9+ KB
+
+
+

We can use the isna()/isnull() method of the series to find nulls:

+
+
contain_nulls = df[
+    df.SNOW.isna() | df.SNWD.isna() | df.TOBS.isna()
+    | df.WESF.isna() | df.inclement_weather.isna()
+]
+contain_nulls.shape[0]
+
+
765
+
+
+
+
contain_nulls.head(10)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datestationPRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
02018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
12018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
22018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
32018-01-02T00:00:00GHCND:USC002809070.00.0-inf-8.3-16.1-12.2NaNFalse
42018-01-03T00:00:00GHCND:USC002809070.00.0-inf-4.4-13.9-13.3NaNFalse
52018-01-03T00:00:00GHCND:USC002809070.00.0-inf-4.4-13.9-13.3NaNFalse
62018-01-03T00:00:00GHCND:USC002809070.00.0-inf-4.4-13.9-13.3NaNFalse
72018-01-04T00:00:00?20.6229.0inf5505.0-40.0NaN19.3True
82018-01-04T00:00:00?20.6229.0inf5505.0-40.0NaN19.3True
92018-01-05T00:00:00?0.3NaNNaN5505.0-40.0NaNNaNNaN
+ +
+
+
+

Note that we can’t check if we have NaN like this:

+
+
df[df.inclement_weather == 'NaN'].shape[0]
+
+
0
+
+
+

This is because it is actually np.nan. However, notice this also doesn’t work:

+
+
import numpy as np
+df[df.inclement_weather == np.nan].shape[0]
+
+
0
+
+
+

We have to use one of the methods discussed earlier for this to work:

+
+
df[df.inclement_weather.isna()].shape[0]
+
+
357
+
+
+

We can find -inf/inf by comparing to -np.inf/np.inf:

+
+
df[df.SNWD.isin([-np.inf, np.inf])].shape[0]
+
+
577
+
+
+

Rather than do this for each column, we can write a function that will use a dictionary comprehension to check all the columns for us:

+
+
def get_inf_count(df):
+    """Find the number of inf/-inf values per column in the dataframe"""
+    return {
+        col: df[df[col].isin([np.inf, -np.inf])].shape[0] for col in df.columns
+    }
+
+get_inf_count(df)
+
+
{'date': 0,
+ 'station': 0,
+ 'PRCP': 0,
+ 'SNOW': 0,
+ 'SNWD': 577,
+ 'TMAX': 0,
+ 'TMIN': 0,
+ 'TOBS': 0,
+ 'WESF': 0,
+ 'inclement_weather': 0}
+
+
+

Before we can decide how to handle the infinite values of snow depth, we should look at the summary statistics for snowfall, which forms a big part in determining the snow depth:

+
+
pd.DataFrame({
+    'np.inf Snow Depth': df[df.SNWD == np.inf].SNOW.describe(),
+    '-np.inf Snow Depth': df[df.SNWD == -np.inf].SNOW.describe()
+}).T
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
countmeanstdmin25%50%75%max
np.inf Snow Depth24.0101.04166774.49801813.025.0120.5152.0229.0
-np.inf Snow Depth553.00.0000000.0000000.00.00.00.00.0
+ +
+
+
+

Let’s now look into the date and station columns. We saw the ? for station earlier, so we know that was the other unique value. However, we see that some dates are present 8 times in the data and we only have 324 days meaning we are also missing days:

+
+
df.describe(include='object')
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datestationinclement_weather
count765765408
unique32422
top2018-07-05T00:00:00GHCND:USC00280907False
freq8398384
+ +
+
+
+

We can use the duplicated() method to find duplicate rows:

+
+
df[df.duplicated()].shape[0]
+
+
284
+
+
+

The default for keep is 'first' meaning it won’t show the first row that the duplicated data was seen in; we can pass in False to see it though:

+
+
df[df.duplicated(keep=False)].shape[0]
+
+
482
+
+
+

We can also specify the columns to use:

+
+
df[df.duplicated(['date', 'station'])].shape[0]
+
+
284
+
+
+

Let’s look at a few duplicates. Just in the few values we see here, we know that the top 4 are actually in the data 6 times because by default we aren’t seeing their first occurrence:

+
+
df[df.duplicated()].head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datestationPRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
12018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
22018-01-01T00:00:00?0.00.0-inf5505.0-40.0NaNNaNNaN
52018-01-03T00:00:00GHCND:USC002809070.00.0-inf-4.4-13.9-13.3NaNFalse
62018-01-03T00:00:00GHCND:USC002809070.00.0-inf-4.4-13.9-13.3NaNFalse
82018-01-04T00:00:00?20.6229.0inf5505.0-40.0NaN19.3True
+ +
+
+
+
+
+

Mitigating Issues

+
+

Handling duplicated data

+

Since we know we have NY weather data and noticed we only had two entries for station, we may decide to drop the station column because we are only interested in the weather data. However, when dealing with duplicate data, we need to think of the ramifications of removing it. Notice we only have data for the WESF column when the station is ?:

+
+
df[df.WESF.notna()].station.unique()
+
+
array(['?'], dtype=object)
+
+
+

If we determine it won’t impact our analysis, we can use drop_duplicates() to remove them:

+
+
# 1. make the date a datetime
+df.date = pd.to_datetime(df.date)
+
+# 2. save this information for later
+station_qm_wesf = df[df.station == '?'].drop_duplicates('date').set_index('date').WESF
+
+# 3. sort ? to the bottom
+df.sort_values('station', ascending=False, inplace=True)
+
+# 4. drop duplicates based on the date column keeping the first occurrence 
+# which will be the valid station if it has data
+df_deduped = df.drop_duplicates('date')
+
+# 5. remove the station column because we are done with it
+df_deduped = df_deduped.drop(columns='station').set_index('date').sort_index()
+
+# 6. take valid station's WESF and fall back on station ? if it is null
+df_deduped = df_deduped.assign(
+    WESF=lambda x: x.WESF.combine_first(station_qm_wesf)
+)
+
+df_deduped.shape
+
+
(324, 8)
+
+
+

Here we used the combine_first() method to coalesce the values to the first non-null entry; this means that if we had data from both stations, we would first take the value provided by the named station and if (and only if) that station was null would we take the value from the station named ?. The following table contains some examples of how this would play out:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
station GHCND:USC00280907station ?result of combine_first()
1171
1NaN1
NaN1717
NaNNaNNaN
+

Check out the 4th row—we have WESF in the correct spot thanks to the index:

+
+
df_deduped.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
date
2018-01-010.00.0-inf5505.0-40.0NaNNaNNaN
2018-01-020.00.0-inf-8.3-16.1-12.2NaNFalse
2018-01-030.00.0-inf-4.4-13.9-13.3NaNFalse
2018-01-0420.6229.0inf5505.0-40.0NaN19.3True
2018-01-0514.2127.0inf-4.4-13.9-13.9NaNTrue
+ +
+
+
+
+
+

Dealing with nulls

+

We could drop nulls, replace them with some arbitrary value, or impute them using the surrounding data. Each of these options may have ramifications, so we must choose wisely.

+

We can use dropna() to drop rows where any column has a null value. The default options leave us hardly any data:

+
+
df_deduped.dropna().shape
+
+
(4, 8)
+
+
+

If we pass how='all', we can choose to only drop rows where everything is null, but this removes nothing:

+
+
df_deduped.dropna(how='all').shape
+
+
(324, 8)
+
+
+

We can use just a subset of columns to determine what to drop with the subset argument:

+
+
df_deduped.dropna(
+    how='all', subset=['inclement_weather', 'SNOW', 'SNWD']
+).shape
+
+
(293, 8)
+
+
+

This can also be performed along columns, and we can also require a certain number of null values before we drop the data:

+
+
df_deduped.dropna(axis='columns', thresh=df_deduped.shape[0] * .75).columns
+
+
Index(['PRCP', 'SNOW', 'SNWD', 'TMAX', 'TMIN', 'TOBS', 'inclement_weather'], dtype='object')
+
+
+

We can choose to fill in the null values instead with fillna():

+
+
df_deduped.loc[:,'WESF'].fillna(0, inplace=True)
+df_deduped.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
date
2018-01-010.00.0-inf5505.0-40.0NaN0.0NaN
2018-01-020.00.0-inf-8.3-16.1-12.20.0False
2018-01-030.00.0-inf-4.4-13.9-13.30.0False
2018-01-0420.6229.0inf5505.0-40.0NaN19.3True
2018-01-0514.2127.0inf-4.4-13.9-13.90.0True
+ +
+
+
+

At this point we have done everything we can without distorting the data. We know that we are missing dates, but if we reindex, we don’t know how to fill in the NaN data. With the weather data, we can’t assume because it snowed one day that it will snow the next or that the temperature will be the same. For this reason, note that the next few examples are just for illustrative purposes only—just because we can do something doesn’t mean we should.

+

That being said, let’s try to address some of remaining issues with the temperature data. We know that when TMAX is the temperature of the Sun, it must be because there was no measured value, so let’s replace it with NaN. We will also do so for TMIN which currently uses -40°C for its placeholder when we know that the coldest temperature ever recorded in NYC was -15°F (-26.1°C) on February 9, 1934:

+
+
df_deduped = df_deduped.assign(
+    TMAX=lambda x: x.TMAX.replace(5505, np.nan),
+    TMIN=lambda x: x.TMIN.replace(-40, np.nan),
+)
+
+

We will also make an assumption that the temperature won’t change drastically day-to-day. Note that this is actually a big assumption, but it will allow us to understand how fillna() works when we provide a strategy through the method parameter. The fillna() method gives us 2 options for the method parameter: - 'ffill' to forward-fill - 'bfill' to back-fill

+

Note that 'nearest' is missing because we are not reindexing.

+

Here, we will use 'ffill' to show how this works:

+
+
df_deduped.assign(
+    TMAX=lambda x: x.TMAX.fillna(method='ffill'),
+    TMIN=lambda x: x.TMIN.fillna(method='ffill')
+).head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
date
2018-01-010.00.0-infNaNNaNNaN0.0NaN
2018-01-020.00.0-inf-8.3-16.1-12.20.0False
2018-01-030.00.0-inf-4.4-13.9-13.30.0False
2018-01-0420.6229.0inf-4.4-13.9NaN19.3True
2018-01-0514.2127.0inf-4.4-13.9-13.90.0True
+ +
+
+
+

We can use np.nan_to_num() to turn np.nan into 0 and -np.inf/np.inf into large negative or positive finite numbers:

+
+
df_deduped.assign(
+    SNWD=lambda x: np.nan_to_num(x.SNWD)
+).head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
date
2018-01-010.00.0-1.797693e+308NaNNaNNaN0.0NaN
2018-01-020.00.0-1.797693e+308-8.3-16.1-12.20.0False
2018-01-030.00.0-1.797693e+308-4.4-13.9-13.30.0False
2018-01-0420.6229.01.797693e+308NaNNaNNaN19.3True
2018-01-0514.2127.01.797693e+308-4.4-13.9-13.90.0True
+ +
+
+
+

Depending on the data we are working with, we can use the clip() method as an alternative to np.nan_to_num(). The clip() method makes it possible to cap values at a specific minimum and/or maximum threshold. Since SNWD can’t be negative, let’s use clip() to enforce a lower bound of zero. To show how the upper bound works, let’s use the value of SNOW:

+
+
df_deduped.assign(
+    SNWD=lambda x: x.SNWD.clip(0, x.SNOW)
+).head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
date
2018-01-010.00.00.0NaNNaNNaN0.0NaN
2018-01-020.00.00.0-8.3-16.1-12.20.0False
2018-01-030.00.00.0-4.4-13.9-13.30.0False
2018-01-0420.6229.0229.0NaNNaNNaN19.3True
2018-01-0514.2127.0127.0-4.4-13.9-13.90.0True
+ +
+
+
+

We can couple fillna() with other types of calculations. Here we replace missing values of TMAX with the median of all TMAX values, TMIN with the median of all TMIN values, and TOBS to the average of the TMAX and TMIN values. Since we place TOBS last, we have access to the imputed values for TMIN and TMAX in the calculation:

+
+
df_deduped.assign(
+    TMAX=lambda x: x.TMAX.fillna(x.TMAX.median()),
+    TMIN=lambda x: x.TMIN.fillna(x.TMIN.median()),
+    # average of TMAX and TMIN
+    TOBS=lambda x: x.TOBS.fillna((x.TMAX + x.TMIN) / 2)
+).head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
date
2018-01-010.00.0-inf14.45.610.00.0NaN
2018-01-020.00.0-inf-8.3-16.1-12.20.0False
2018-01-030.00.0-inf-4.4-13.9-13.30.0False
2018-01-0420.6229.0inf14.45.610.019.3True
2018-01-0514.2127.0inf-4.4-13.9-13.90.0True
+ +
+
+
+

We can also use apply() for running the same calculation across columns. For example, let’s fill all missing values with their rolling 7-day median of their values, setting the number of periods required for the calculation to 0 to ensure we don’t introduce more extra NaN values. Rolling calculations will be covered later on, so this is a preview:

+
+
df_deduped.apply(
+    # rolling calculations will be covered later on, this is a rolling 7-day median
+    # we set min_periods (# of periods required for calculation) to 0 so we always get a result 
+    lambda x: x.fillna(x.rolling(7, min_periods=0).median())
+).head(10)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
date
2018-01-010.00.0-infNaNNaNNaN0.0NaN
2018-01-020.00.0-inf-8.30-16.1-12.200.0False
2018-01-030.00.0-inf-4.40-13.9-13.300.0False
2018-01-0420.6229.0inf-6.35-15.0-12.7519.3True
2018-01-0514.2127.0inf-4.40-13.9-13.900.0True
2018-01-060.00.0-inf-10.00-15.6-15.000.0False
2018-01-070.00.0-inf-11.70-17.2-16.100.0False
2018-01-080.00.0-inf-7.80-16.7-8.300.0False
2018-01-100.00.0-inf5.00-7.8-7.800.0False
2018-01-110.00.0-inf4.40-7.81.100.0False
+ +
+
+
+

The last strategy we could try is interpolation with the interpolate() method. We specify the method parameter with the interpolation strategy to use. There are many options, but we will stick with the default of 'linear', which will treat values as evenly spaced and place missing values in the middle of existing ones. We have some missing data, so we will reindex first. Look at January 9th, which we didn’t have before—the values for TMAX, TMIN, and TOBS are the average of values the day prior (January 8th) and the day after (January 10th):

+
+
df_deduped\
+    .reindex(pd.date_range('2018-01-01', '2018-12-31', freq='D'))\
+    .apply(lambda x: x.interpolate())\
+    .head(10)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PRCPSNOWSNWDTMAXTMINTOBSWESFinclement_weather
2018-01-010.00.0-infNaNNaNNaN0.0NaN
2018-01-020.00.0-inf-8.3-16.10-12.200.0False
2018-01-030.00.0-inf-4.4-13.90-13.300.0False
2018-01-0420.6229.0inf-4.4-13.90-13.6019.3True
2018-01-0514.2127.0inf-4.4-13.90-13.900.0True
2018-01-060.00.0-inf-10.0-15.60-15.000.0False
2018-01-070.00.0-inf-11.7-17.20-16.100.0False
2018-01-080.00.0-inf-7.8-16.70-8.300.0False
2018-01-090.00.0-inf-1.4-12.25-8.050.0NaN
2018-01-100.00.0-inf5.0-7.80-7.800.0False
+ +
+
+
+ + +
+
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/docs/notebooks/hypothesis_testing.html b/docs/notebooks/hypothesis_testing.html new file mode 100644 index 0000000..4247146 --- /dev/null +++ b/docs/notebooks/hypothesis_testing.html @@ -0,0 +1,577 @@ + + + + + + + + + +Hypothesis Testing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+

Hypothesis Testing

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Hypothesis Testing

+

A hypothesis is a claim or statement about a parameter1. There are two types of statistical hypotheses: - Null Hypothesis - Alternative Hypothesis

+

A null hypothesis is a statement that claims that the parameter is equal to some claimed value. - H0 is the symbol used to denote this. It can be pronounced as “H null”, “H zero” or “H nought” - It always contains one of these operators: \(\ge\), \(\le\), =. - This value is the one to always assume is true.

+

An alternative hypothesis is a statement that claims that the parameter is a different value than the null. - HA or H1 is the symbol used to denote this. It’s always called “alternative hypothesis.” - It always contains one of these operators: \(\gt\), \(\lt\), \(\neq\).

+
+

Steps to Solving a Hypothesis Test Problem

+
    +
  1. Write and label everything.
  2. +
  3. Write hypotheses: +
      +
    • H0: (operator with equal sign)
    • +
    • HA: (operator without equal sign)
    • +
  4. +
  5. Draw graph (bell-curved) +
      +
    • The graph will either be right, left or two tailed.
    • +
  6. +
  7. Carry out the necessary calculations to arrive to a solution. +
      +
    • This can involve solving a t-statisic or z-test.
    • +
  8. +
  9. Write a sentence summarizing the findings. +
      +
    • Usually follows this format: “There is/is not sufficient evidence to support/reject the claim that…”
    • +
  10. +
+
+
+

Rejection Explained

+

Every hypothesis test is rejected or failed to reject. This is because we either have enough data to be able to say the hypothesis is correct, or we don’t have enough data to prove otherwise. To determine this, we compare the significance level to the p-value .

+

The significance level is denoted by \(\alpha\) which measures how strong the evidence must be in order to determine the evidence to be statistically significant.

+

P-value is defined by Investopedia as “a statistical measurement used to validate a hypothesis against observed data.” We’re not going to go in-depth here regarding how the p-value is calculated, but just enough to scratch the surface. This value describes the likelihood of the data occurring randomly. P-values range from 0 to 1 and a smaller p-value denotes a smaller probability that the results occurred randomly.

+
    +
  • If p-value \(\leq\) \(\alpha\), then reject H0.
  • +
  • If p-value \(\gt\) \(\alpha\), then fail to reject H0.
  • +
+
+
+

Determining the Tail of the Curve

+

The trick to remembering where the tail of the curve is by looking at the alternative hypothesis. - If the sign in HA is - \(\neq\): two-tailed - \(\lt\): left-taied - \(\gt\): right-tailed

+
+
+

Errors

+

Sometimes, error occurs with hypothesis testing and there are two types of it: - Type I error - This is known as the false-positive. - It occurs when the null hypothesis is rejected, but it is true. - Type II error - This is known as the false-negative. - It occurs when the null hypothesis is not rejected, but it is false.

+

This table below from Scribbr can be used to determine error type, if any.

+

+

1A parameter is a measure done on an entire population of data.

+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/docs/notebooks/intro_to_plotly_express.html b/docs/notebooks/intro_to_plotly_express.html new file mode 100644 index 0000000..e65989a --- /dev/null +++ b/docs/notebooks/intro_to_plotly_express.html @@ -0,0 +1,878 @@ + + + + + + + + + +intro_to_plotly_express + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + + +
+
import plotly.express as px
+
+df = px.data.gapminder().query("year==2007")
+
+
+
df.columns
+
+
Index(['country', 'continent', 'year', 'lifeExp', 'pop', 'gdpPercap',
+       'iso_alpha', 'iso_num'],
+      dtype='object')
+
+
+
+
df.describe()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearlifeExppopgdpPercapiso_num
count142.0142.0000001.420000e+02142.000000142.000000
mean2007.067.0074234.402122e+0711680.071820425.880282
std0.012.0730211.476214e+0812859.937337249.111541
min2007.039.6130001.995790e+05277.5518594.000000
25%2007.057.1602504.508034e+061624.842248209.500000
50%2007.071.9355001.051753e+076124.371109410.000000
75%2007.076.4132503.121004e+0718008.835640636.000000
max2007.082.6030001.318683e+0949357.190170894.000000
+ +
+
+
+
+
px.strip(df, x='lifeExp', hover_name="country")
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.strip(df, x='lifeExp', color="continent", hover_name="country")
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.histogram(df, x='lifeExp', color="continent", hover_name="country")
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.histogram(df, x='lifeExp', color="continent", hover_name="country", marginal="rug")
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.histogram(df, x='lifeExp', y="pop", color="continent", hover_name="country", marginal="rug")
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.histogram(df, x='lifeExp', y="pop", color="continent", hover_name="country", marginal="rug", facet_col="continent")
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.bar(df, color='lifeExp', x="pop", y="continent", hover_name="country")
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.sunburst(df, color='lifeExp', values="pop", path=["continent", "country"], hover_name="country", height=500)
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.treemap(df, color='lifeExp', values="pop", path=["continent", "country"], hover_name="country", height=500)
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.choropleth(df, color='lifeExp', locations="iso_alpha", hover_name="country", height=500)
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.scatter(df, x="gdpPercap", y='lifeExp', hover_name="country", height=500)
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
px.scatter(df, x="gdpPercap", y='lifeExp', hover_name="country", color="continent",size="pop", height=500)
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+

We can see that the curve follows a logarithmic path, so make log_x=True to straighten out the line to view the relationships in an easier manner. In the graph below we can view the monotic and nonmonotonic relationships in the dataset.

+
+
px.scatter(df, x="gdpPercap", y='lifeExp', hover_name="country", color="continent",size="pop", size_max=60, log_x=True, height=500)
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
fig = px.scatter(df, x="gdpPercap", y='lifeExp', hover_name="country", color="continent",size="pop", size_max=60, log_x=True, height=500)
+
+

This will allow you to inspect the values for each of these cells, unfortunately this is a great deal easier to see in JupyterLab.

+
+
fig.show("json")
+
+
Unable to display output for mime type(s): application/json
+
+
+
+
import plotly.express as px
+
+df = px.data.gapminder().query("year == 2007")
+
+fig = px.scatter(df, x="gdpPercap",y="lifeExp", color="continent", log_x=True, size="pop", size_max=60,
+                 hover_name="country", height=600, width=1000, template="simple_white", 
+                 color_discrete_sequence=px.colors.qualitative.G10,
+                 title="Health vs Wealth 2007",
+                 labels=dict(
+                     continent="Continent", pop="Population",
+                     gdpPercap="GDP per Capita (US$, price-adjusted)", 
+                     lifeExp="Life Expectancy (years)"))
+
+fig.update_layout(font_family="Rockwell",
+                  legend=dict(orientation="h", title="", y=1.1, x=1, xanchor="right", yanchor="bottom"))
+fig.update_xaxes(tickprefix="$", range=[2,5], dtick=1)
+fig.update_yaxes(range=[30,90])
+fig.add_hline((df["lifeExp"]*df["pop"]).sum()/df["pop"].sum(), line_width=1, line_dash="dot")
+fig.add_vline((df["gdpPercap"]*df["pop"]).sum()/df["pop"].sum(), line_width=1, line_dash="dot")
+fig.show()
+
+# fig.write_image("gapminder_2007.svg") # static export
+# fig.write_html("gapminder_2007.html") # interactive export
+# fig.write_json("gapminder_2007.json") # serialized export
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+

Animations in Plotly Express

+
+
df_animation = px.data.gapminder()
+
+anim_fig = px.scatter(df_animation, x="gdpPercap", y="lifeExp",
+                      title="Health vs Wealth from 1952 to 2007",
+                      labels=dict(continent="Continent", pop="Population", gdpPercap="GDP per Capita (US$, price-adjusted)", lifeExp="Life Expectancy (years)"),
+                      animation_frame="year", animation_group="country",
+                      size="pop",
+                      color="continent",
+                      hover_name="country",
+                      height=600,width=1000,
+                      template="simple_white",
+                      color_discrete_sequence=px.colors.qualitative.G10,
+                      log_x=True,
+                      size_max=60,
+                      range_x=[100,100000],
+                      range_y=[25,90])
+
+anim_fig.update_layout(font_family="Rockwell",
+                  legend=dict(orientation="h", title="", y=1.1, x=1, xanchor="right", yanchor="bottom"))
+anim_fig.update_xaxes(tickprefix="$", range=[2,5], dtick=1)
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
anim_fig.write_html("gapminder_animation.html", auto_play=False) # You're able to export this animation.
+
+
+
px.defaults.height=600
+
+
+
import plotly.express as px
+
+z = [[.1, .3, .5, .7, .9],
+     [1, .8, .6, .4, .2],
+     [.2, 0, .5, .7, .9],
+     [.9, .8, .4, .2, 0],
+     [.3, .4, .5, .7, 1]]
+
+fig = px.imshow(z, text_auto=True)
+fig.show()
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
import plotly.express as px
+df = px.data.wind()
+fig = px.bar_polar(df, r="frequency", theta="direction", height=600,
+                   color="strength", template="plotly_dark",
+                   color_discrete_sequence= px.colors.sequential.Plasma_r)
+fig.show()
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
df = px.data.iris()
+fig = px.parallel_coordinates(df, color="species_id", labels={"species_id": "Species",
+                  "sepal_width": "Sepal Width", "sepal_length": "Sepal Length",
+                  "petal_width": "Petal Width", "petal_length": "Petal Length", },
+                    color_continuous_scale=px.colors.diverging.Tealrose, color_continuous_midpoint=2)
+fig.show()
+
+
c:\Users\gpower\Anaconda3\envs\cary_dev\lib\site-packages\plotly\express\_core.py:279: FutureWarning:
+
+iteritems is deprecated and will be removed in a future version. Use .items instead.
+
+
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
df = px.data.tips()
+fig = px.parallel_categories(df, color="size", color_continuous_scale=px.colors.sequential.Inferno)
+fig.show()
+
+
c:\Users\gpower\Anaconda3\envs\cary_dev\lib\site-packages\plotly\express\_core.py:279: FutureWarning:
+
+iteritems is deprecated and will be removed in a future version. Use .items instead.
+
+
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
df = px.data.iris()
+fig = px.parallel_coordinates(df, color="species_id", labels={"species_id": "Species",
+                  "sepal_width": "Sepal Width", "sepal_length": "Sepal Length",
+                  "petal_width": "Petal Width", "petal_length": "Petal Length", },
+                    color_continuous_scale=px.colors.diverging.Tealrose, color_continuous_midpoint=2)
+fig.show()
+
+
c:\Users\gpower\Anaconda3\envs\cary_dev\lib\site-packages\plotly\express\_core.py:279: FutureWarning:
+
+iteritems is deprecated and will be removed in a future version. Use .items instead.
+
+
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
df = px.data.tips()
+fig = px.parallel_categories(df, color="size", color_continuous_scale=px.colors.sequential.Inferno)
+fig.show()
+
+
c:\Users\gpower\Anaconda3\envs\cary_dev\lib\site-packages\plotly\express\_core.py:279: FutureWarning:
+
+iteritems is deprecated and will be removed in a future version. Use .items instead.
+
+
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
df = px.data.election()
+fig = px.scatter_ternary(df, a="Joly", b="Coderre", c="Bergeron", color="winner", size="total", hover_name="district",
+                   size_max=15, color_discrete_map = {"Joly": "blue", "Bergeron": "green", "Coderre":"red"} )
+fig.show()
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+
+
df = px.data.election()
+fig = px.scatter_3d(df, x="Joly", y="Coderre", z="Bergeron", color="winner", size="total", hover_name="district",
+                  symbol="result", color_discrete_map = {"Joly": "blue", "Bergeron": "green", "Coderre":"red"})
+fig.show()
+
+
Unable to display output for mime type(s): application/vnd.plotly.v1+json
+
+
+ + +
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/docs/notebooks/introducing_matplotlib.html b/docs/notebooks/introducing_matplotlib.html new file mode 100644 index 0000000..791764c --- /dev/null +++ b/docs/notebooks/introducing_matplotlib.html @@ -0,0 +1,858 @@ + + + + + + + + + +Getting Started with Matplotlib + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+

Getting Started with Matplotlib

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Getting Started with Matplotlib

+

Pandas uses matplotlib to create visualizations. Therefore, before we learn how to plot with pandas, it’s important to understand how matplotlib works at a high-level, which is the focus of this notebook.

+
+

About the Data

+

In this notebook, we will be working with 2 datasets: - Facebook’s stock price throughout 2018 (obtained using the stock_analysis package) - Earthquake data from September 18, 2018 - October 13, 2018 (obtained from the US Geological Survey (USGS) using the USGS API)

+
+
+

Setup

+

We need to import matplotlib.pyplot for plotting.

+
+
import matplotlib.pyplot as plt
+import pandas as pd
+
+
+
+

Plotting lines

+
+
fb = pd.read_csv(
+    '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True
+)
+
+plt.plot(fb.index, fb.open)
+plt.show()
+
+
+
+

+
+
+
+
+

Since we are working in a Jupyter notebook, we can use the magic command %matplotlib inline once and not have to call plt.show() for each plot.

+
+
%matplotlib inline
+import matplotlib.pyplot as plt
+import pandas as pd
+
+fb = pd.read_csv(
+    '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True
+)
+plt.plot(fb.index, fb.open)
+
+
+
+

+
+
+
+
+
+
+

Scatter plots

+

We can pass in a string specifying the style of the plot. This is of the form [marker][linestyle][color]. For example, we can make a black dashed line with '--k' or a red scatter plot with 'or':

+
+
plt.plot('high', 'low', 'or', data=fb.head(20))
+
+
+
+

+
+
+
+
+

Here are some examples of how you make a format string:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MarkerLinestyleColorFormat StringResult
-b-bblue solid line
.k.kblack points
--r--rred dashed line
o-go-ggreen solid line with circles
:m:mmagenta dotted line
x-.cx-.ccyan dot-dashed line with x’s
+

Note that we can also use format strings of the form [color][marker][linestyle], but the parsing by matplotlib (in rare cases) might not be what we were aiming for. Consult the Notes section in the documentation for the complete list of options. ## Histograms

+
+
quakes = pd.read_csv('../data/earthquakes.csv')
+plt.hist(quakes.query('magType == "ml"').mag)
+
+
(array([6.400e+01, 4.450e+02, 1.137e+03, 1.853e+03, 2.114e+03, 8.070e+02,
+        2.800e+02, 9.200e+01, 9.000e+00, 2.000e+00]),
+ array([-1.26 , -0.624,  0.012,  0.648,  1.284,  1.92 ,  2.556,  3.192,
+         3.828,  4.464,  5.1  ]),
+ <BarContainer object of 10 artists>)
+
+
+
+
+

+
+
+
+
+
+

Bin size matters

+

Notice how our assumptions of the distribution of the data can change based on the number of bins (look at the drop between the two highest peaks on the righthand plot):

+
+
x = quakes.query('magType == "ml"').mag
+fig, axes = plt.subplots(1, 2, figsize=(10, 3))
+for ax, bins in zip(axes, [7, 35]):
+    ax.hist(x, bins=bins)
+    ax.set_title(f'bins param: {bins}')
+
+
+
+

+
+
+
+
+
+
+
+

Plot components

+
+

Figure

+

Top-level object that holds the other plot components.

+
+
fig = plt.figure()
+
+
<Figure size 640x480 with 0 Axes>
+
+
+
+
+

Axes

+

Individual plots contained within the Figure.

+
+
+
+

Creating subplots

+

Simply specify the number of rows and columns to create:

+
+
fig, axes = plt.subplots(1, 2)
+
+
+
+

+
+
+
+
+

As an alternative to using plt.subplots() we can add Axes objects to the Figure object on our own. This allows for some more complex layouts, such as picture in picture:

+
+
fig = plt.figure(figsize=(3, 3))
+outside = fig.add_axes([0.1, 0.1, 0.9, 0.9])
+inside = fig.add_axes([0.7, 0.7, 0.25, 0.25])
+
+
+
+

+
+
+
+
+
+
+

Creating Plot Layouts with gridspec

+

We can create subplots with varying sizes as well:

+
+
fig = plt.figure(figsize=(8, 8))
+gs = fig.add_gridspec(3, 3)
+top_left = fig.add_subplot(gs[0, 0])
+mid_left = fig.add_subplot(gs[1, 0])
+top_right = fig.add_subplot(gs[:2, 1:])
+bottom = fig.add_subplot(gs[2,:])
+
+
+
+

+
+
+
+
+
+
+

Saving plots

+

Use plt.savefig() to save the last created plot. To save a specific Figure object, use its savefig() method. Which supports ‘png’, ‘pdf’, ‘svg’, and ‘eps’ filetypes.

+
+
fig.savefig('empty.png')
+fig.savefig('empty.pdf')
+fig.savefig('empty.svg')
+fig.savefig('empty.eps')
+
+
+
+

Cleaning up

+

It’s important to close resources when we are done with them. We use plt.close() to do so. If we pass in nothing, it will close the last plot, but we can pass in the specific Figure object to close or say 'all' to close all Figure objects that are open. Let’s close all the Figure objects that are open with plt.close():

+
+
plt.close('all')
+
+
+
+

Additional plotting options

+
+

Specifying figure size

+

Just pass the figsize argument to plt.figure(). It’s a tuple of (width, height):

+
+
fig = plt.figure(figsize=(10, 4))
+
+
<Figure size 1000x400 with 0 Axes>
+
+
+

This can be specified when creating subplots as well:

+
+
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
+
+
+
+

+
+
+
+
+
+
+

rcParams

+

A small subset of all the available plot settings (shuffling to get a good variation of options):

+
+
import random
+import matplotlib as mpl
+
+rcparams_list = list(mpl.rcParams.keys())
+random.seed(20) # make this repeatable
+random.shuffle(rcparams_list)
+sorted(rcparams_list[:20])
+
+
['animation.convert_args',
+ 'axes.edgecolor',
+ 'axes.formatter.use_locale',
+ 'axes.spines.right',
+ 'boxplot.meanprops.markersize',
+ 'boxplot.showfliers',
+ 'keymap.home',
+ 'lines.markerfacecolor',
+ 'lines.scale_dashes',
+ 'mathtext.rm',
+ 'patch.force_edgecolor',
+ 'savefig.facecolor',
+ 'svg.fonttype',
+ 'text.hinting_factor',
+ 'xtick.alignment',
+ 'xtick.minor.top',
+ 'xtick.minor.width',
+ 'ytick.left',
+ 'ytick.major.left',
+ 'ytick.minor.width']
+
+
+

We can check the current default figsize using rcParams:

+
+
mpl.rcParams['figure.figsize']
+
+
[6.4, 4.8]
+
+
+

We can also update this value to change the default (until the kernel is restarted):

+
+
mpl.rcParams['figure.figsize'] = (300, 10)
+mpl.rcParams['figure.figsize']
+
+
[300.0, 10.0]
+
+
+

Use rcdefaults() to restore the defaults. Note this is slightly different than before because running %matplotlib inline sets a different value for figsize (see more). After we reset, we are going back to the default value of figsize before that import:

+
+
mpl.rcdefaults()
+mpl.rcParams['figure.figsize']
+
+
[6.4, 4.8]
+
+
+

This can also be done via pyplot:

+
+
plt.rc('figure', figsize=(20, 20)) # change `figsize` default to (20, 20)
+plt.rcdefaults() # reset the default
+
+ + +
+
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/docs/notebooks/introducing_matplotlib_files/figure-html/cell-10-output-1.png b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-10-output-1.png new file mode 100644 index 0000000..22f358a Binary files /dev/null and b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-10-output-1.png differ diff --git a/docs/notebooks/introducing_matplotlib_files/figure-html/cell-11-output-1.png b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-11-output-1.png new file mode 100644 index 0000000..13f4aa6 Binary files /dev/null and b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-11-output-1.png differ diff --git a/docs/notebooks/introducing_matplotlib_files/figure-html/cell-15-output-1.png b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-15-output-1.png new file mode 100644 index 0000000..1c91b7a Binary files /dev/null and b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-15-output-1.png differ diff --git a/docs/notebooks/introducing_matplotlib_files/figure-html/cell-3-output-1.png b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-3-output-1.png new file mode 100644 index 0000000..eb03fb8 Binary files /dev/null and b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-3-output-1.png differ diff --git a/docs/notebooks/introducing_matplotlib_files/figure-html/cell-4-output-1.png b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-4-output-1.png new file mode 100644 index 0000000..eb03fb8 Binary files /dev/null and b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-4-output-1.png differ diff --git a/docs/notebooks/introducing_matplotlib_files/figure-html/cell-5-output-1.png b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-5-output-1.png new file mode 100644 index 0000000..77304c0 Binary files /dev/null and b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-5-output-1.png differ diff --git a/docs/notebooks/introducing_matplotlib_files/figure-html/cell-6-output-2.png b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-6-output-2.png new file mode 100644 index 0000000..df8cdf0 Binary files /dev/null and b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-6-output-2.png differ diff --git a/docs/notebooks/introducing_matplotlib_files/figure-html/cell-7-output-1.png b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-7-output-1.png new file mode 100644 index 0000000..f3ec1f7 Binary files /dev/null and b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-7-output-1.png differ diff --git a/docs/notebooks/introducing_matplotlib_files/figure-html/cell-9-output-1.png b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-9-output-1.png new file mode 100644 index 0000000..1540129 Binary files /dev/null and b/docs/notebooks/introducing_matplotlib_files/figure-html/cell-9-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis.html b/docs/notebooks/introduction_to_data_analysis.html new file mode 100644 index 0000000..110277b --- /dev/null +++ b/docs/notebooks/introduction_to_data_analysis.html @@ -0,0 +1,912 @@ + + + + + + + + + +Introduction to Data Analysis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+

Introduction to Data Analysis

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Introduction to Data Analysis

+

This notebook serves as a summary of the fundamentals. For a Python crash-course/refresher, work through the python_101.ipynb notebook.

+
+

Setup

+
+
import sys
+sys.path.append('../src/')
+sys.dont_write_bytecode = True
+
+import stats_viz
+
+
+
+

Fundamentals of data analysis

+

When conducting a data analysis, we will move back and forth between four main processes:

+
    +
  • Data Collection: Every analysis starts with collecting data. We can collect data from a variety of sources, including databases, APIs, flat files, and the Internet.
  • +
  • Data Wrangling: After we have our data, we need to prepare it for our analysis. This may involve reshaping it, changing data types, handling missing values, and/or aggregating it.
  • +
  • Exploratory Data Analysis (EDA): We can use visualizations to explore our data and summarize it. During this time, we will also begin exploring the data by looking at its structure, format, and summary statistics.
  • +
  • Drawing Conclusions: After we have thoroughly explored our data, we can try to draw conclusions or model it.
  • +
+
+
+

Statistical Foundations

+

As this is an overview of statistics, we will discuss some concepts. By no means is this exhaustive.

+
+

Sampling

+

Some resampling (sampling from the sample) techniques you will see: - simple random sampling: pick with a random number generator - stratified random sampling: randomly pick preserving the proportion of groups in the data - bootstrapping: sampling with replacement (more info: YouTube video and Wikipedia article)

+
+
+

Descriptive Statistics

+

We use descriptive statistics to describe the data. The data we work with is usually a sample taken from the population. The statistics we will discuss here are referred to as sample statistics because they are calculated on the sample and can be used as estimators for the population parameters.

+
+

Measures of Center

+

Three common ways to describe the central tendency of a distribution are mean, median, and mode. ##### Mean The sample mean is an estimator for the population mean (\(\mu\)) and is defined as:

+

\[\bar{x} = \frac{\sum_{1}^{n} x_i}{n}\] ##### Median The median represents the 50th percentile of our data; this means that 50% of the values are greater than the median and 50% are less than the median. It is calculated by taking the middle value from an ordered list of values.

+
+
Mode
+

The mode is the most common value in the data. We can use it to describe categorical data or, for continuous data, the shape of the distribution:

+
+
ax = stats_viz.different_modal_plots()
+
+
+
+

+
+
+
+
+
+
+
+

Measures of Spread

+

Measures of spread tell us how the data is dispersed; this will indicate how thin (low dispersion) or wide (very spread out) our distribution is.

+
+
Range
+

The range is the distance between the smallest value (minimum) and the largest value (maximum):

+

\[range = max(X) - min(X)\]

+
+
+
Variance
+

The variance describes how far apart observations are spread out from their average value (the mean). When calculating the sample variance, we divide by n - 1 instead of n to account for using the sample mean (\(\bar{x}\)):

+

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

+

This is referred to as Bessel’s correction and is applied to get an unbiased estimator of the population variance.

+

Note that this will be in units-squared of whatever was being measured.

+
+
+
Standard Deviation
+

The standard deviation is the square root of the variance, giving us a measure in the same units as our data. The sample standard deviation is calculated as follows:

+

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

+
+
ax = stats_viz.effect_of_std_dev()
+
+
+
+

+
+
+
+
+

Note that \(\sigma^2\) is the population variance and \(\sigma\) is the population standard deviation.

+
+
+
Coefficient of Variation
+

The coefficient of variation (CV) gives us a unitless ratio of the standard deviation to the mean. Since, it has no units we can compare dispersion across datasets:

+

\[CV = \frac{s}{\bar{x}}\]

+
+
+
Interquartile Range
+

The interquartile range (IQR) gives us the spread of data around the median and quantifies how much dispersion we have in the middle 50% of our distribution:

+

\[IQR = Q_3 - Q_1\]

+
+
+
Quartile Coefficient of Dispersion
+

The quartile coefficient of dispersion also is a unitless statistic for comparing datasets. However, it uses the median as the measure of center. It is calculated by dividing the semi-quartile range (half the IQR) by the midhinge (midpoint between the first and third quartiles):

+

\[QCD = \frac{\frac{Q_3 - Q_1}{2}}{\frac{Q_1 + Q_3}{2}} = \frac{Q_3 - Q_1}{Q_3 + Q_1}\]

+
+
+
+

Summarizing data

+

The 5-number summary provides 5 descriptive statistics that summarize our data:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
QuartileStatisticPercentile
1.\(Q_0\)minimum\(0^{th}\)
2.\(Q_1\)N/A\(25^{th}\)
3.\(Q_2\)median\(50^{th}\)
4.\(Q_3\)N/A\(75^{th}\)
5.\(Q_4\)maximum\(100^{th}\)
+

This summary can be visualized using a box plot (also called box-and-whisker plot). The box has an upper bound of \(Q_3\) and a lower bound of \(Q_1\). The median will be a line somewhere in this box. The whiskers extend from the box towards the minimum/maximum. For our purposes, they will extend to \(Q_3 + 1.5 \times IQR\) and \(Q_1 - 1.5 \times IQR\) and anything beyond will be represented as individual points for outliers:

+
+
ax = stats_viz.example_boxplot()
+
+
+
+

+
+
+
+
+

The box plot doesn’t show us how the data is distributed within the quartiles. To get a better sense of the distribution, we can use a histogram, which will show us the amount of observations that fall into equal-width bins. We can vary the number of bins to use, but be aware that this can change our impression of what the distribution appears to be:

+
+
ax = stats_viz.example_histogram()
+
+
+
+

+
+
+
+
+

We can also visualize the distribution using a kernel density estimate (KDE). This will estimate the probability density function (PDF). This function shows how probability is distributed over the values. Higher values of the PDF mean higher likelihoods:

+
+
ax = stats_viz.example_kde()
+
+
+
+

+
+
+
+
+

Note that both the KDE and histogram estimate the distribution:

+
+
ax = stats_viz.hist_and_kde()
+
+
+
+

+
+
+
+
+

Skewed distributions have more observations on one side. The mean will be less than the median with negative skew, while the opposite is true of positive skew:

+
+
ax = stats_viz.skew_examples()
+
+
+
+

+
+
+
+
+

We can use the cumulative distribution function (CDF) to find probabilities of getting values within a certain range. The CDF is the integral of the PDF:

+

\[CDF = F(x) = \int_{-\infty}^{x} f(t) dt\]

+

Note that \(f(t)\) is the PDF and \(\int_{-\infty}^{\infty} f(t) dt = 1\).

+

The probability of the random variable \(X\) being less than or equal to the specific value of \(x\) is denoted as \(P(X ≤ x)\). Note that for a continuous random variable the probability of it being exactly \(x\) is zero.

+

Let’s look at the estimate of the CDF from the sample data we used for the box plot, called the empirical cumulative distribution function (ECDF):

+
+
ax = stats_viz.cdf_example()
+
+
+
+

+
+
+
+
+

We can find any range we want if we use some algebra as in the rightmost subplot above.

+
+
+

Common Distributions

+
    +
  • Gaussian (normal) distribution: looks like a bell curve and is parameterized by its mean (μ) and standard deviation (σ). Many things in nature happen to follow the normal distribution, like heights. Note that testing if a distribution is normal is not trivial. Written as \(N(\mu, \sigma)\).
  • +
  • Poisson distribution: discrete distribution that is often used to model arrivals. Parameterized by its mean, lambda (λ). Written as \(Pois(\lambda)\).
  • +
  • Exponential distribution: can be used to model the time between arrivals. Parameterized by its mean, lambda (λ). Written as \(Exp(\lambda)\).
  • +
  • Uniform distribution: places equal likelihood on each value within its bounds (a and b). We often use this for random number generation. Written as \(U(a, b)\).
  • +
  • Bernoulli distribution: When we pick a random number to simulate a single success/failure outcome, it is called a Bernoulli trial. This is parameterized by the probability of success (p). Written as \(Bernoulli(p)\).
  • +
  • Binomial distribution: When we run the same experiment n times, the total number of successes is then a binomial random variable. Written as \(B(n, p)\).
  • +
+

We can visualize both discrete and continuous distributions; however, discrete distributions give us a probability mass function (PMF) instead of a PDF:

+
+
ax = stats_viz.common_dists()
+
+
+
+

+
+
+
+
+
+
+

Scaling data

+

In order to compare variables from different distributions, we would have to scale the data, which we could do with the range by using min-max scaling:

+

\[x_{scaled}=\frac{x - min(X)}{range(X)}\]

+

Another way is to use a Z-score to standardize the data:

+

\[z_i = \frac{x_i - \bar{x}}{s}\]

+
+
+

Quantifying relationships between variables

+

The covariance is a statistic for quantifying the relationship between variables by showing how one variable changes with respect to another (also referred to as their joint variance):

+

\[cov(X, Y) = E[(X-E[X])(Y-E[Y])]\]

+

E[X] is the expectation of the random variable X (its long-run average).

+

The sign of the covariance gives us the direction of the relationship, but we need the magnitude as well. For that, we calculate the Pearson correlation coefficient (\(\rho\)):

+

\[\rho_{X, Y} = \frac{cov(X, Y)}{s_X s_Y}\]

+

Examples:

+
+
ax = stats_viz.correlation_coefficient_examples()
+
+
+
+

+
+
+
+
+

From left to right: no correlation, weak negative correlation, strong positive correlation, and nearly perfect negative correlation.

+

Often, it is more informative to use scatter plots to check for relationships between variables. This is because the correlation may be strong, but the relationship may not be linear:

+
+
ax = stats_viz.non_linear_relationships()
+
+
+
+

+
+
+
+
+

Remember, correlation does not imply causation. While we may find a correlation between X and Y, it does not mean that X causes Y or Y causes X. It is possible there is some Z that causes both or that X causes some intermediary event that causes Y — it could even be a coincidence. Be sure to check out Tyler Vigen’s Spurious Correlations blog for some interesting correlations.

+
+
+

Pitfalls of summary statistics

+

Not only can our correlation coefficients be misleading, but so can summary statistics. Anscombe’s quartet is a collection of four different datasets that have identical summary statistics and correlation coefficients, however, when plotted, it is obvious they are not similar:

+
+
ax = stats_viz.anscombes_quartet()
+
+
+
+

+
+
+
+
+

Another example of this is the Datasaurus Dozen:

+
+
ax = stats_viz.datasaurus_dozen()
+
+
+
+

+
+
+
+
+
+
+
+

Prediction and forecasting

+

Say our favorite ice cream shop has asked us to help predict how many ice creams they can expect to sell on a given day. They are convinced that the temperature outside has strong influence on their sales, so they collected data on the number of ice creams sold at a given temperature. We agree to help them, and the first thing we do is make a scatter plot of the data they gave us:

+
+
ax = stats_viz.example_scatter_plot()
+
+
+
+

+
+
+
+
+

We can observe an upward trend in the scatter plot: more ice creams are sold at higher temperatures. In order to help out the ice cream shop, though, we need to find a way to make predictions from this data. We can use a technique called regression to model the relationship between temperature and ice cream sales with an equation:

+
+
ax = stats_viz.example_regression()
+
+
+
+

+
+
+
+
+

We can use the resulting equation to make predictions for the number of ice creams sold at various temperatures. However, we must keep in mind if we are interpolating or extrapolating. If the temperature value we are using for prediction is within the range of the original data we used to build our regression model, then we are interpolating (solid portion of the red line). On the other hand, if the temperature is beyond the values in the original data, we are extrapolating, which is very dangerous, since we can’t assume the pattern continues indefinitely in each direction (dotted portion of the line). Extremely hot temperatures may cause people to stay inside, meaning no ice creams will be sold, while the equation indicates record-high sales.

+

Forecasting is a type of prediction for time series. In a process called time series decomposition, time series is decomposed into a trend component, a seasonality component, and a cyclical component. These components can be combined in an additive or multiplicative fashion:

+
+
ax = stats_viz.time_series_decomposition_example()
+
+
+
+

+
+
+
+
+

The trend component describes the behavior of the time series in the long-term without accounting for the seasonal or cyclical effects. Using the trend, we can make broad statements about the time series in the long-run, such as: the population of Earth is increasing or the value of a stock is stagnating. Seasonality of a time series explains the systematic and calendar-related movements of a time series. For example, the number of ice cream trucks on the streets of New York City is high in the summer and drops to nothing in the winter; this pattern repeats every year regardless of whether the actual amount each summer is the same. Lastly, the cyclical component accounts for anything else unexplained or irregular with the time series; this could be something like a hurricane driving the number of ice cream trucks down in the short-term because it isn’t safe to be outside. This component is difficult to anticipate with a forecast due to its unexpected nature.

+

When making models to forecast time series, some common methods include ARIMA-family methods and exponential smoothing. ARIMA stands for autoregressive (AR), integrated (I), moving average (MA). Autoregressive models take advantage of the fact that an observation at time \(t\) is correlated to a previous observation, for example at time \(t - 1\). Note that not all time series are autoregressive. The integrated component concerns the differenced data, or the change in the data from one time to another. Lastly, the moving average component uses a sliding window to average the last \(x\) observations where \(x\) is the length of the sliding window.

+

The moving average puts equal weight on each time period in the past involved in the calculation. In practice, this isn’t always a realistic expectation of our data. Sometimes all past values are important, but they vary in their influence on future data points. For these cases, we can use exponential smoothing, which allows us to put more weight on more recent values and less weight on values further away from what we are predicting.

+
+
+

Inferential Statistics

+

Inferential statistics deals with inferring or deducing things from the sample data we have in order to make statements about the population as a whole. Before doing so, we need to know whether we conducted an observational study or an experiment. An observational study can’t be used to determine causation because we can’t control for everything. An experiment on the other hand is controlled.

+

Remember that the sample statistics we discussed earlier are estimators for the population parameters. Our estimators need confidence intervals, which provide a point estimate and a margin of error around it. This is the range that the true population parameter will be in at a certain confidence level. At the 95% confidence level, 95% of the confidence intervals calculated from random samples of the population contain the true population parameter.

+

We also have the option of using hypothesis testing. First, we define a null hypothesis (say the true population mean is 0), then we determine a significance level (1 - confidence level), which is the probability of rejecting the null hypothesis when it is true. Our result is statistically significant if the value for the null hypothesis is outside the confidence interval. More info.

+ + +
+
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-10-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-10-output-1.png new file mode 100644 index 0000000..b88bf8f Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-10-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-11-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-11-output-1.png new file mode 100644 index 0000000..5ecea84 Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-11-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-12-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-12-output-1.png new file mode 100644 index 0000000..43cfda1 Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-12-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-13-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-13-output-1.png new file mode 100644 index 0000000..960a92f Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-13-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-14-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-14-output-1.png new file mode 100644 index 0000000..d27f574 Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-14-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-15-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-15-output-1.png new file mode 100644 index 0000000..ea51cd9 Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-15-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-16-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-16-output-1.png new file mode 100644 index 0000000..c86a3f1 Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-16-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-17-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-17-output-1.png new file mode 100644 index 0000000..0c7875b Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-17-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-18-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-18-output-1.png new file mode 100644 index 0000000..bfffa46 Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-18-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-3-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-3-output-1.png new file mode 100644 index 0000000..24236e5 Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-3-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-4-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-4-output-1.png new file mode 100644 index 0000000..73da7f6 Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-4-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-5-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-5-output-1.png new file mode 100644 index 0000000..fd220d2 Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-5-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-6-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-6-output-1.png new file mode 100644 index 0000000..9fcc164 Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-6-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-7-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-7-output-1.png new file mode 100644 index 0000000..aea5b49 Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-7-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-8-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-8-output-1.png new file mode 100644 index 0000000..8e7f355 Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-8-output-1.png differ diff --git a/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-9-output-1.png b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-9-output-1.png new file mode 100644 index 0000000..30a503b Binary files /dev/null and b/docs/notebooks/introduction_to_data_analysis_files/figure-html/cell-9-output-1.png differ diff --git a/docs/notebooks/making_dataframes_from_api_requests.html b/docs/notebooks/making_dataframes_from_api_requests.html new file mode 100644 index 0000000..8ff43d2 --- /dev/null +++ b/docs/notebooks/making_dataframes_from_api_requests.html @@ -0,0 +1,804 @@ + + + + + + + + + +Making Pandas DataFrames from API Requests + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+

Making Pandas DataFrames from API Requests

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Making Pandas DataFrames from API Requests

+

In this example, we will use the U.S. Geological Survey’s API to grab a JSON object of earthquake data and convert it to a pandas.DataFrame.

+

USGS API: https://earthquake.usgs.gov/fdsnws/event/1/

+
+

Get Data from API

+
+
import datetime as dt
+import pandas as pd
+import requests
+
+yesterday = dt.date.today() - dt.timedelta(days=1)
+api = 'https://earthquake.usgs.gov/fdsnws/event/1/query'
+payload = {
+    'format': 'geojson',
+    'starttime': yesterday - dt.timedelta(days=30),
+    'endtime': yesterday
+}
+response = requests.get(api, params=payload)
+
+# let's make sure the request was OK
+response.status_code
+
+
200
+
+
+

Response of 200 means OK, so we can pull the data out of the result. Since we asked the API for a JSON payload, we can extract it from the response with the json() method.

+
+
+

Isolate the Data from the JSON Response

+

We need to check the structures of the response data to know where our data is.

+
+
earthquake_json = response.json()
+earthquake_json.keys()
+
+
dict_keys(['type', 'metadata', 'features', 'bbox'])
+
+
+

The USGS API provides information about our request in the metadata key. Note that your result will be different, regardless of the date range you chose, because the API includes a timestamp for when the data was pulled:

+
+
earthquake_json['metadata']
+
+
{'generated': 1686247399000,
+ 'url': 'https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2023-05-08&endtime=2023-06-07',
+ 'title': 'USGS Earthquakes',
+ 'status': 200,
+ 'api': '1.14.0',
+ 'count': 11706}
+
+
+

Each element in the JSON array features is a row of data for our dataframe.

+
+
type(earthquake_json['features'])
+
+
list
+
+
+

Your data will be different depending on the date you run this.

+
+
earthquake_json['features'][0]
+
+
{'type': 'Feature',
+ 'properties': {'mag': 0.5,
+  'place': '17km SE of Anza, CA',
+  'time': 1686095356120,
+  'updated': 1686178472172,
+  'tz': None,
+  'url': 'https://earthquake.usgs.gov/earthquakes/eventpage/ci40479680',
+  'detail': 'https://earthquake.usgs.gov/fdsnws/event/1/query?eventid=ci40479680&format=geojson',
+  'felt': None,
+  'cdi': None,
+  'mmi': None,
+  'alert': None,
+  'status': 'reviewed',
+  'tsunami': 0,
+  'sig': 4,
+  'net': 'ci',
+  'code': '40479680',
+  'ids': ',ci40479680,',
+  'sources': ',ci,',
+  'types': ',nearby-cities,origin,phase-data,scitech-link,',
+  'nst': 21,
+  'dmin': 0.07016,
+  'rms': 0.26,
+  'gap': 86,
+  'magType': 'ml',
+  'type': 'earthquake',
+  'title': 'M 0.5 - 17km SE of Anza, CA'},
+ 'geometry': {'type': 'Point', 'coordinates': [-116.5405, 33.447, 11.37]},
+ 'id': 'ci40479680'}
+
+
+
+
+

Convert to DataFrame

+

We need to grab the properties section out of every entry in the features JSON array to create our dataframe.

+
+
earthquake_properties_data = [
+    quake['properties'] for quake in earthquake_json['features']
+]
+df = pd.DataFrame(earthquake_properties_data)
+df.head()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
magplacetimeupdatedtzurldetailfeltcdimmi...idssourcestypesnstdminrmsgapmagTypetypetitle
00.5017km SE of Anza, CA16860953561201686178472172Nonehttps://earthquake.usgs.gov/earthquakes/eventp...https://earthquake.usgs.gov/fdsnws/event/1/que...NaNNaNNaN...,ci40479680,,ci,,nearby-cities,origin,phase-data,scitech-link,21.00.0701600.2686.0mlearthquakeM 0.5 - 17km SE of Anza, CA
11.246 km W of Blanchard, Oklahoma16860952889001686140204481Nonehttps://earthquake.usgs.gov/earthquakes/eventp...https://earthquake.usgs.gov/fdsnws/event/1/que...0.01.0NaN...,ok2023lavp,,ok,,dyfi,origin,phase-data,64.00.0890830.4442.0mlearthquakeM 1.2 - 6 km W of Blanchard, Oklahoma
25.20southeast of the Loyalty Islands16860946935721686095577040Nonehttps://earthquake.usgs.gov/earthquakes/eventp...https://earthquake.usgs.gov/fdsnws/event/1/que...NaNNaNNaN...,us7000k6rg,,us,,origin,phase-data,32.02.7550000.8285.0mbearthquakeM 5.2 - southeast of the Loyalty Islands
30.2410 km NNE of Government Camp, Oregon16860946115901686118209350Nonehttps://earthquake.usgs.gov/earthquakes/eventp...https://earthquake.usgs.gov/fdsnws/event/1/que...NaNNaNNaN...,uw61931036,,uw,,origin,phase-data,3.00.0326200.04239.0mlearthquakeM 0.2 - 10 km NNE of Government Camp, Oregon
4-0.018 km NNE of Government Camp, Oregon16860941173101686117953180Nonehttps://earthquake.usgs.gov/earthquakes/eventp...https://earthquake.usgs.gov/fdsnws/event/1/que...NaNNaNNaN...,uw61931031,,uw,,origin,phase-data,4.00.0115100.04173.0mlearthquakeM 0.0 - 8 km NNE of Government Camp, Oregon
+ +

5 rows × 26 columns

+
+
+
+
+
+

(Optional) Write Data to CSV

+
+
df.to_csv('earthquakes.csv', index=False)
+
+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/docs/notebooks/one_hot_encoding.html b/docs/notebooks/one_hot_encoding.html new file mode 100644 index 0000000..4aae83c --- /dev/null +++ b/docs/notebooks/one_hot_encoding.html @@ -0,0 +1,2193 @@ + + + + + + + + + +One-Hot Encoding + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+

One-Hot Encoding

+
+ + + +
+ + + + +
+ + + +
+ + +
+

One-Hot Encoding

+
+

Why Bother with One-Hot Encoding?

+

It’s useful for feeding categorical data into machine-learning algorithms since integers are computationally less expensive than strings.

+
+
import pandas as pd
+print(pd.__version__)
+
+
2.0.3
+
+
+
+
disengagements = pd.read_excel("../data/cassi-autonomous-shuttle/autonomous_shuttle_disengagement.xlsx",usecols=["Incident Datetime", "Location","Weather","Vehicle Speed in Miles per Hour", "Initiated by","Cause"], parse_dates=True)
+disengagements
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Incident DatetimeLocationWeatherVehicle Speed in Miles per HourInitiated byCause
02023-03-07T10:00:00-05:0035.7849964, -78.8268094Sunny;2OperatorFault Code/Error Code
12023-03-07T14:00:00-05:0035.7847312, -78.8245051Sunny;5OperatorStation Blocked
22023-03-07T14:30:00-05:0035.7824658, -78.8244159Sunny;5OperatorStation Blocked
32023-03-07T15:15:00-05:0035.7824658, -78.8244159Sunny;4OperatorStation Blocked
42023-03-08T10:00:00-05:0035.7852558, -78.8273737Sunny;2OperatorShuttle Manually Deviated from Approved Path
.....................
1742023-06-01T16:00:00-04:0035.783456, -78.821639Sunny;5OperatorSignal Loss
1752023-06-02T10:32:00-04:0035.7819145, -78.8235603Sunny;4OperatorStation Blocked
1762023-06-02T10:35:00-04:0035.7813188, -78.8256601Sunny;3OperatorStation Blocked
1772023-06-02T10:44:00-04:0035.7847325, -78.824496Sunny;4OperatorObstacle Detection
1782023-06-02T11:01:00-04:0035.7841086, -78.8261962Sunny;3OperatorSignalized Intersection
+ +

179 rows × 6 columns

+
+
+
+
+
disengagements.dtypes
+
+
Incident Datetime                  object
+Location                           object
+Weather                            object
+Vehicle Speed in Miles per Hour     int64
+Initiated by                       object
+Cause                              object
+dtype: object
+
+
+
+
disengagements['Incident Datetime'] = pd.to_datetime(disengagements['Incident Datetime'], utc=True)
+disengagements['Initiated by'] = disengagements['Initiated by'].astype('category')
+disengagements['Cause'] = disengagements['Cause'].astype('category')
+disengagements.dtypes
+
+
Incident Datetime                  datetime64[ns, UTC]
+Location                                        object
+Weather                                         object
+Vehicle Speed in Miles per Hour                  int64
+Initiated by                                  category
+Cause                                         category
+dtype: object
+
+
+
+
disengagements = disengagements.assign(week_of_year = disengagements['Incident Datetime'].dt.isocalendar().week, week_of_pilot = lambda x: disengagements['Incident Datetime'].dt.isocalendar().week - 9)
+disengagements
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Incident DatetimeLocationWeatherVehicle Speed in Miles per HourInitiated byCauseweek_of_yearweek_of_pilot
02023-03-07 15:00:00+00:0035.7849964, -78.8268094Sunny;2OperatorFault Code/Error Code101
12023-03-07 19:00:00+00:0035.7847312, -78.8245051Sunny;5OperatorStation Blocked101
22023-03-07 19:30:00+00:0035.7824658, -78.8244159Sunny;5OperatorStation Blocked101
32023-03-07 20:15:00+00:0035.7824658, -78.8244159Sunny;4OperatorStation Blocked101
42023-03-08 15:00:00+00:0035.7852558, -78.8273737Sunny;2OperatorShuttle Manually Deviated from Approved Path101
...........................
1742023-06-01 20:00:00+00:0035.783456, -78.821639Sunny;5OperatorSignal Loss2213
1752023-06-02 14:32:00+00:0035.7819145, -78.8235603Sunny;4OperatorStation Blocked2213
1762023-06-02 14:35:00+00:0035.7813188, -78.8256601Sunny;3OperatorStation Blocked2213
1772023-06-02 14:44:00+00:0035.7847325, -78.824496Sunny;4OperatorObstacle Detection2213
1782023-06-02 15:01:00+00:0035.7841086, -78.8261962Sunny;3OperatorSignalized Intersection2213
+ +

179 rows × 8 columns

+
+
+
+
+
disengagements['Cause']
+
+
0                             Fault Code/Error Code
+1                                   Station Blocked
+2                                   Station Blocked
+3                                   Station Blocked
+4      Shuttle Manually Deviated from Approved Path
+                           ...                     
+174                                     Signal Loss
+175                                 Station Blocked
+176                                 Station Blocked
+177                              Obstacle Detection
+178                         Signalized Intersection
+Name: Cause, Length: 179, dtype: category
+Categories (9, object): ['Fault Code/Error Code', 'Obstacle Detection', 'Other Road Users', 'Priority Zone', ..., 'Signal Loss', 'Signalized Intersection', 'Station Blocked', 'Vegetation']
+
+
+
+
disengagements['Cause'].cat.categories
+
+
Index(['Fault Code/Error Code', 'Obstacle Detection', 'Other Road Users',
+       'Priority Zone', 'Shuttle Manually Deviated from Approved Path',
+       'Signal Loss', 'Signalized Intersection', 'Station Blocked',
+       'Vegetation'],
+      dtype='object')
+
+
+
+
disengagements_datetime_is_index = disengagements.set_index('Incident Datetime')
+disengagements_datetime_is_index
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LocationWeatherVehicle Speed in Miles per HourInitiated byCauseweek_of_yearweek_of_pilot
Incident Datetime
2023-03-07 15:00:00+00:0035.7849964, -78.8268094Sunny;2OperatorFault Code/Error Code101
2023-03-07 19:00:00+00:0035.7847312, -78.8245051Sunny;5OperatorStation Blocked101
2023-03-07 19:30:00+00:0035.7824658, -78.8244159Sunny;5OperatorStation Blocked101
2023-03-07 20:15:00+00:0035.7824658, -78.8244159Sunny;4OperatorStation Blocked101
2023-03-08 15:00:00+00:0035.7852558, -78.8273737Sunny;2OperatorShuttle Manually Deviated from Approved Path101
........................
2023-06-01 20:00:00+00:0035.783456, -78.821639Sunny;5OperatorSignal Loss2213
2023-06-02 14:32:00+00:0035.7819145, -78.8235603Sunny;4OperatorStation Blocked2213
2023-06-02 14:35:00+00:0035.7813188, -78.8256601Sunny;3OperatorStation Blocked2213
2023-06-02 14:44:00+00:0035.7847325, -78.824496Sunny;4OperatorObstacle Detection2213
2023-06-02 15:01:00+00:0035.7841086, -78.8261962Sunny;3OperatorSignalized Intersection2213
+ +

179 rows × 7 columns

+
+
+
+
+
disengagements_datetime_is_index.index=disengagements_datetime_is_index.index.tz_convert(tz='US/Eastern')
+disengagements_datetime_is_index
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LocationWeatherVehicle Speed in Miles per HourInitiated byCauseweek_of_yearweek_of_pilot
Incident Datetime
2023-03-07 10:00:00-05:0035.7849964, -78.8268094Sunny;2OperatorFault Code/Error Code101
2023-03-07 14:00:00-05:0035.7847312, -78.8245051Sunny;5OperatorStation Blocked101
2023-03-07 14:30:00-05:0035.7824658, -78.8244159Sunny;5OperatorStation Blocked101
2023-03-07 15:15:00-05:0035.7824658, -78.8244159Sunny;4OperatorStation Blocked101
2023-03-08 10:00:00-05:0035.7852558, -78.8273737Sunny;2OperatorShuttle Manually Deviated from Approved Path101
........................
2023-06-01 16:00:00-04:0035.783456, -78.821639Sunny;5OperatorSignal Loss2213
2023-06-02 10:32:00-04:0035.7819145, -78.8235603Sunny;4OperatorStation Blocked2213
2023-06-02 10:35:00-04:0035.7813188, -78.8256601Sunny;3OperatorStation Blocked2213
2023-06-02 10:44:00-04:0035.7847325, -78.824496Sunny;4OperatorObstacle Detection2213
2023-06-02 11:01:00-04:0035.7841086, -78.8261962Sunny;3OperatorSignalized Intersection2213
+ +

179 rows × 7 columns

+
+
+
+
+
disengagements_datetime_is_index.dtypes
+
+
Location                             object
+Weather                              object
+Vehicle Speed in Miles per Hour       int64
+Initiated by                       category
+Cause                              category
+week_of_year                         UInt32
+week_of_pilot                        UInt32
+dtype: object
+
+
+
+
one_hot = disengagements_datetime_is_index.Weather.str.get_dummies(sep=';')
+one_hot
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CloudyPartly CloudyRainSunnyWindy
Incident Datetime
2023-03-07 10:00:00-05:0000010
2023-03-07 14:00:00-05:0000010
2023-03-07 14:30:00-05:0000010
2023-03-07 15:15:00-05:0000010
2023-03-08 10:00:00-05:0000010
..................
2023-06-01 16:00:00-04:0000010
2023-06-02 10:32:00-04:0000010
2023-06-02 10:35:00-04:0000010
2023-06-02 10:44:00-04:0000010
2023-06-02 11:01:00-04:0000010
+ +

179 rows × 5 columns

+
+
+
+
+
one_hot.columns = 'Weather_' + one_hot.columns
+one_hot
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Weather_CloudyWeather_Partly CloudyWeather_RainWeather_SunnyWeather_Windy
Incident Datetime
2023-03-07 10:00:00-05:0000010
2023-03-07 14:00:00-05:0000010
2023-03-07 14:30:00-05:0000010
2023-03-07 15:15:00-05:0000010
2023-03-08 10:00:00-05:0000010
..................
2023-06-01 16:00:00-04:0000010
2023-06-02 10:32:00-04:0000010
2023-06-02 10:35:00-04:0000010
2023-06-02 10:44:00-04:0000010
2023-06-02 11:01:00-04:0000010
+ +

179 rows × 5 columns

+
+
+
+
+
one_hot_cause = disengagements_datetime_is_index.Cause.str.get_dummies()
+one_hot_cause.columns = 'Cause_' + one_hot_cause.columns
+one_hot_cause
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Cause_Fault Code/Error CodeCause_Obstacle DetectionCause_Other Road UsersCause_Priority ZoneCause_Shuttle Manually Deviated from Approved PathCause_Signal LossCause_Signalized IntersectionCause_Station BlockedCause_Vegetation
Incident Datetime
2023-03-07 10:00:00-05:00100000000
2023-03-07 14:00:00-05:00000000010
2023-03-07 14:30:00-05:00000000010
2023-03-07 15:15:00-05:00000000010
2023-03-08 10:00:00-05:00000010000
..............................
2023-06-01 16:00:00-04:00000001000
2023-06-02 10:32:00-04:00000000010
2023-06-02 10:35:00-04:00000000010
2023-06-02 10:44:00-04:00010000000
2023-06-02 11:01:00-04:00000000100
+ +

179 rows × 9 columns

+
+
+
+
+
disengagements_datetime_is_index = disengagements_datetime_is_index.drop(['Weather', 'Initiated by', 'Cause'], axis=1)
+cassi_data_one_hot_encoded = pd.concat([disengagements_datetime_is_index, one_hot, one_hot_cause], axis=1)
+cassi_data_one_hot_encoded
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LocationVehicle Speed in Miles per Hourweek_of_yearweek_of_pilotWeather_CloudyWeather_Partly CloudyWeather_RainWeather_SunnyWeather_WindyCause_Fault Code/Error CodeCause_Obstacle DetectionCause_Other Road UsersCause_Priority ZoneCause_Shuttle Manually Deviated from Approved PathCause_Signal LossCause_Signalized IntersectionCause_Station BlockedCause_Vegetation
Incident Datetime
2023-03-07 10:00:00-05:0035.7849964, -78.8268094210100010100000000
2023-03-07 14:00:00-05:0035.7847312, -78.8245051510100010000000010
2023-03-07 14:30:00-05:0035.7824658, -78.8244159510100010000000010
2023-03-07 15:15:00-05:0035.7824658, -78.8244159410100010000000010
2023-03-08 10:00:00-05:0035.7852558, -78.8273737210100010000010000
.........................................................
2023-06-01 16:00:00-04:0035.783456, -78.8216395221300010000001000
2023-06-02 10:32:00-04:0035.7819145, -78.82356034221300010000000010
2023-06-02 10:35:00-04:0035.7813188, -78.82566013221300010000000010
2023-06-02 10:44:00-04:0035.7847325, -78.8244964221300010010000000
2023-06-02 11:01:00-04:0035.7841086, -78.82619623221300010000000100
+ +

179 rows × 18 columns

+
+
+
+
+
cassi_data_one_hot_encoded.index = cassi_data_one_hot_encoded.index.tz_convert(tz='UTC')
+
+
+
cassi_data_one_hot_encoded
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LocationVehicle Speed in Miles per Hourweek_of_yearweek_of_pilotWeather_CloudyWeather_Partly CloudyWeather_RainWeather_SunnyWeather_WindyCause_Fault Code/Error CodeCause_Obstacle DetectionCause_Other Road UsersCause_Priority ZoneCause_Shuttle Manually Deviated from Approved PathCause_Signal LossCause_Signalized IntersectionCause_Station BlockedCause_Vegetation
Incident Datetime
2023-03-07 15:00:00+00:0035.7849964, -78.8268094210100010100000000
2023-03-07 19:00:00+00:0035.7847312, -78.8245051510100010000000010
2023-03-07 19:30:00+00:0035.7824658, -78.8244159510100010000000010
2023-03-07 20:15:00+00:0035.7824658, -78.8244159410100010000000010
2023-03-08 15:00:00+00:0035.7852558, -78.8273737210100010000010000
.........................................................
2023-06-01 20:00:00+00:0035.783456, -78.8216395221300010000001000
2023-06-02 14:32:00+00:0035.7819145, -78.82356034221300010000000010
2023-06-02 14:35:00+00:0035.7813188, -78.82566013221300010000000010
2023-06-02 14:44:00+00:0035.7847325, -78.8244964221300010010000000
2023-06-02 15:01:00+00:0035.7841086, -78.82619623221300010000000100
+ +

179 rows × 18 columns

+
+
+
+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/docs/notebooks/pandas_plotting_module.html b/docs/notebooks/pandas_plotting_module.html new file mode 100644 index 0000000..efa4b5d --- /dev/null +++ b/docs/notebooks/pandas_plotting_module.html @@ -0,0 +1,713 @@ + + + + + + + + + +The pandas.plotting module + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+

The pandas.plotting module

+
+ + + +
+ + + + +
+ + + +
+ + +
+

The pandas.plotting module

+

Pandas provides some extra plotting functions for some new plot types.

+
+

About the Data

+

In this notebook, we will be working with Facebook’s stock price throughout 2018 (obtained using the stock_analysis package).

+
+
+

Setup

+
+
%matplotlib inline
+import matplotlib.pyplot as plt
+import numpy as np
+import pandas as pd
+
+fb = pd.read_csv(
+    '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True
+)
+
+
+
+

Scatter matrix

+

Easily create scatter plots between all columns in the dataset:

+
+
from pandas.plotting import scatter_matrix
+scatter_matrix(fb, figsize=(10, 10))
+
+
array([[<Axes: xlabel='open', ylabel='open'>,
+        <Axes: xlabel='high', ylabel='open'>,
+        <Axes: xlabel='low', ylabel='open'>,
+        <Axes: xlabel='close', ylabel='open'>,
+        <Axes: xlabel='volume', ylabel='open'>],
+       [<Axes: xlabel='open', ylabel='high'>,
+        <Axes: xlabel='high', ylabel='high'>,
+        <Axes: xlabel='low', ylabel='high'>,
+        <Axes: xlabel='close', ylabel='high'>,
+        <Axes: xlabel='volume', ylabel='high'>],
+       [<Axes: xlabel='open', ylabel='low'>,
+        <Axes: xlabel='high', ylabel='low'>,
+        <Axes: xlabel='low', ylabel='low'>,
+        <Axes: xlabel='close', ylabel='low'>,
+        <Axes: xlabel='volume', ylabel='low'>],
+       [<Axes: xlabel='open', ylabel='close'>,
+        <Axes: xlabel='high', ylabel='close'>,
+        <Axes: xlabel='low', ylabel='close'>,
+        <Axes: xlabel='close', ylabel='close'>,
+        <Axes: xlabel='volume', ylabel='close'>],
+       [<Axes: xlabel='open', ylabel='volume'>,
+        <Axes: xlabel='high', ylabel='volume'>,
+        <Axes: xlabel='low', ylabel='volume'>,
+        <Axes: xlabel='close', ylabel='volume'>,
+        <Axes: xlabel='volume', ylabel='volume'>]], dtype=object)
+
+
+
+
+

+
+
+
+
+

Changing the diagonal from histograms to KDE:

+
+
scatter_matrix(fb, figsize=(10, 10), diagonal='kde')
+
+
array([[<Axes: xlabel='open', ylabel='open'>,
+        <Axes: xlabel='high', ylabel='open'>,
+        <Axes: xlabel='low', ylabel='open'>,
+        <Axes: xlabel='close', ylabel='open'>,
+        <Axes: xlabel='volume', ylabel='open'>],
+       [<Axes: xlabel='open', ylabel='high'>,
+        <Axes: xlabel='high', ylabel='high'>,
+        <Axes: xlabel='low', ylabel='high'>,
+        <Axes: xlabel='close', ylabel='high'>,
+        <Axes: xlabel='volume', ylabel='high'>],
+       [<Axes: xlabel='open', ylabel='low'>,
+        <Axes: xlabel='high', ylabel='low'>,
+        <Axes: xlabel='low', ylabel='low'>,
+        <Axes: xlabel='close', ylabel='low'>,
+        <Axes: xlabel='volume', ylabel='low'>],
+       [<Axes: xlabel='open', ylabel='close'>,
+        <Axes: xlabel='high', ylabel='close'>,
+        <Axes: xlabel='low', ylabel='close'>,
+        <Axes: xlabel='close', ylabel='close'>,
+        <Axes: xlabel='volume', ylabel='close'>],
+       [<Axes: xlabel='open', ylabel='volume'>,
+        <Axes: xlabel='high', ylabel='volume'>,
+        <Axes: xlabel='low', ylabel='volume'>,
+        <Axes: xlabel='close', ylabel='volume'>,
+        <Axes: xlabel='volume', ylabel='volume'>]], dtype=object)
+
+
+
+
+

+
+
+
+
+
+
+

Lag plot

+

Lag plots let us see how the variable correlates with past observations of itself. Random data has no pattern:

+
+
from pandas.plotting import lag_plot
+np.random.seed(0) # make this repeatable
+lag_plot(pd.Series(np.random.random(size=200)))
+
+
+
+

+
+
+
+
+

Data with some level of correlation to itself (autocorrelation) may have patterns. Stock prices are highly autocorrelated:

+
+
lag_plot(fb.close)
+
+
+
+

+
+
+
+
+

The default lag is 1, but we can alter this with the lag parameter. Let’s look at a 5 day lag (a week of trading activity):

+
+
lag_plot(fb.close, lag=5)
+
+
+
+

+
+
+
+
+
+
+

Autocorrelation plots

+

We can use the autocorrelation plot to see if this relationship may be meaningful or is just noise. Random data will not have any significant autocorrelation (it stays within the bounds below):

+
+
from pandas.plotting import autocorrelation_plot
+np.random.seed(0) # make this repeatable
+autocorrelation_plot(pd.Series(np.random.random(size=200)))
+
+
+
+

+
+
+
+
+

Stock data, on the other hand, does have significant autocorrelation:

+
+
autocorrelation_plot(fb.close)
+
+
+
+

+
+
+
+
+
+
+

Bootstrap plot

+

This plot helps us understand the uncertainty in our summary statistics:

+
+
from pandas.plotting import bootstrap_plot
+fig = bootstrap_plot(fb.volume, fig=plt.figure(figsize=(10, 6)))
+
+
+
+

+
+
+
+
+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/docs/notebooks/pandas_plotting_module_files/figure-html/cell-10-output-1.png b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-10-output-1.png new file mode 100644 index 0000000..66eddd5 Binary files /dev/null and b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-10-output-1.png differ diff --git a/docs/notebooks/pandas_plotting_module_files/figure-html/cell-3-output-2.png b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-3-output-2.png new file mode 100644 index 0000000..e769890 Binary files /dev/null and b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-3-output-2.png differ diff --git a/docs/notebooks/pandas_plotting_module_files/figure-html/cell-4-output-2.png b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-4-output-2.png new file mode 100644 index 0000000..09998c3 Binary files /dev/null and b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-4-output-2.png differ diff --git a/docs/notebooks/pandas_plotting_module_files/figure-html/cell-5-output-1.png b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-5-output-1.png new file mode 100644 index 0000000..ef65ea8 Binary files /dev/null and b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-5-output-1.png differ diff --git a/docs/notebooks/pandas_plotting_module_files/figure-html/cell-6-output-1.png b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-6-output-1.png new file mode 100644 index 0000000..5ef94df Binary files /dev/null and b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-6-output-1.png differ diff --git a/docs/notebooks/pandas_plotting_module_files/figure-html/cell-7-output-1.png b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-7-output-1.png new file mode 100644 index 0000000..d9e172b Binary files /dev/null and b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-7-output-1.png differ diff --git a/docs/notebooks/pandas_plotting_module_files/figure-html/cell-8-output-1.png b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-8-output-1.png new file mode 100644 index 0000000..c3cf500 Binary files /dev/null and b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-8-output-1.png differ diff --git a/docs/notebooks/pandas_plotting_module_files/figure-html/cell-9-output-1.png b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-9-output-1.png new file mode 100644 index 0000000..c508a36 Binary files /dev/null and b/docs/notebooks/pandas_plotting_module_files/figure-html/cell-9-output-1.png differ diff --git a/docs/notebooks/plotting_with_pandas.html b/docs/notebooks/plotting_with_pandas.html new file mode 100644 index 0000000..14b1c7f --- /dev/null +++ b/docs/notebooks/plotting_with_pandas.html @@ -0,0 +1,1389 @@ + + + + + + + + + +Plotting with Pandas + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+

Plotting with Pandas

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Plotting with Pandas

+

The plot() method is available on Series and DataFrame objects. Many of the parameters get passed down to matplotlib. The kind argument let’s us vary the plot type. Here are some commonly used parameters:

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterPurposeData Type
kindDetermines the plot typeString
x/yColumn(s) to plot on the x-axis/y-axisString or list
axDraws the plot on the Axes object providedAxes
subplotsDetermines whether to make subplotsBoolean
layoutSpecifies how to arrange the subplotsTuple of (rows, columns)
figsizeSize to make the Figure objectTuple of (width, height)
titleThe title of the plot or subplotsString for the plot title or a list of strings for subplot titles
legendDetermines whether to show the legendBoolean
labelWhat to call an item in the legendString if a single column is being plotted; otherwise, a list of strings
stylematplotlib style strings for each item being plottedString if a single column is being plotted; otherwise, a list of strings
colorThe color to plot the item inString or red, green, blue tuple if a single column is being plotted; otherwise, a list
colormapThe colormap to useString or matplotlib colormap object
logx/logy/loglogDetermines whether to use a logarithmic scale for the x-axis, y-axis, or bothBoolean
xticks/yticksDetermines where to draw the ticks on the x-axis/y-axisList of values
xlim/ylimThe axis limits for the x-axis/y-axisTuple of the form (min, max)
rotThe angle to write the tick labels atInteger
sharex/shareyDetermines whether to have subplots share the x-axis/y-axisBoolean
fontsizeControls the size of the tick labelsInteger
gridTurns on/off the grid linesBoolean
+
+

About the Data

+

In this notebook, we will be working with 3 datasets: - Facebook’s stock price throughout 2018 (obtained using the stock_analysis package) - Earthquake data from September 18, 2018 - October 13, 2018 (obtained from the US Geological Survey (USGS) using the USGS API) - European Centre for Disease Prevention and Control’s (ECDC) daily number of new reported cases of COVID-19 by country worldwide dataset collected on September 19, 2020 via this link

+
+
+

Setup

+
+
%matplotlib inline
+import matplotlib.pyplot as plt
+import numpy as np
+import pandas as pd
+
+fb = pd.read_csv(
+    '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True
+)
+quakes = pd.read_csv('../data/earthquakes.csv')
+covid = pd.read_csv('../data/covid19_cases.csv').assign(
+    date=lambda x: pd.to_datetime(x.dateRep, format='%d/%m/%Y')
+).set_index('date').replace(
+    'United_States_of_America', 'USA'
+).sort_index()['2020-01-18':'2020-09-18']
+
+
+
+

Evolution over time

+

Line plots help us see how a variable changes over time. They are the default for the kind argument, but we can pass kind='line' to be explicit in our intent:

+
+
fb.plot(
+    kind='line',
+    y='open',
+    figsize=(10, 5),
+    style='-b',
+    legend=False,
+    title='Evolution of Facebook Open Price'
+)
+
+
+
+

+
+
+
+
+

We provided the style argument in the previous example; however, we can use the color and linestyle arguments to get the same result:

+
+
fb.plot(
+    kind='line',
+    y='open',
+    figsize=(10, 5),
+    color='blue',
+    linestyle='solid',
+    legend=False,
+    title='Evolution of Facebook Open Price'
+)
+
+
+
+

+
+
+
+
+

We can also plot many lines at once by simply passing a list of the columns to plot:

+
+
fb.first('1W').plot(
+    y=['open', 'high', 'low', 'close'],
+    style=['o-b', '--r', ':k', '.-g'],
+    title='Facebook OHLC Prices during 1st Week of Trading 2018'
+).autoscale()
+
+
+
+

+
+
+
+
+
+

Creating subplots

+

When plotting with pandas, creating subplots is simply a matter of passing subplots=True to the plot() method, and (optionally) specifying the layout in a tuple of (rows, columns):

+
+
fb.plot(
+    kind='line',
+    subplots=True,
+    layout=(3, 2),
+    figsize=(15, 10),
+    title='Facebook Stock 2018'
+)
+
+
array([[<Axes: xlabel='date'>, <Axes: xlabel='date'>],
+       [<Axes: xlabel='date'>, <Axes: xlabel='date'>],
+       [<Axes: xlabel='date'>, <Axes: xlabel='date'>]], dtype=object)
+
+
+
+
+

+
+
+
+
+

Note that we didn’t provide a specific column to plot and pandas plotted all of them for us.

+

Sometimes we want to make subplots that each have a few variables in them for comparison. This can be achieved using the ax parameter. To illustrate this, let’s take a look at daily new COVID-19 cases in China, Spain, Italy, the USA, Brazil, and India:

+
+
new_cases_rolling_average = covid.pivot_table(
+    index=covid.index, 
+    columns='countriesAndTerritories', 
+    values='cases'
+).rolling(7).mean()
+
+

Since there is a lot of fluctuation in these values, we will plot the 7-day moving average of new cases using the rolling() method (discussed in chapter 4). Rather than create a separate plot for each country (which makes it harder to compare) or plot them all together (which will make it difficult to see the smaller values), we will plot countries that have had a similar number of cases in the same subplot:

+
+
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
+
+new_cases_rolling_average[['China']].plot(ax=axes[0], style='-.c')
+new_cases_rolling_average[['Italy', 'Spain']].plot(
+    ax=axes[1], style=['-', '--'], 
+    title='7-day rolling average of new COVID-19 cases\n(source: ECDC)'
+)
+new_cases_rolling_average[['Brazil', 'India', 'USA']]\
+    .plot(ax=axes[2], style=['--', ':', '-'])
+
+
+
+

+
+
+
+
+

NOTE: we specified the line styles here so that the lines can be distinguished in the text as a black and white image.

+

In the previous figure, we were able to compare countries with similar levels of new COVID-19 cases, but we couldn’t compare all of them in the same plot due to scale. One way around this is to use an area plot, which makes it possible for us to visualize the overall 7-day rolling average of new COVID-19 cases and at the same time how much each country is contributing to the total. In the interest of readability, we will group Italy and Spain together and create another category for countries other than the USA, Brazil, and India. The combined height of the plot areas is the overall value, and the height of given shaded region is the value for the individual country.

+
+
plot_cols = ['Brazil', 'India', 'Italy & Spain', 'USA', 'Other']
+grouped = ['Italy', 'Spain']
+other_cols = [
+    col for col in new_cases_rolling_average.columns 
+    if col not in plot_cols
+]
+
+new_cases_rolling_average.sort_index(axis=1).assign(
+    **{
+        'Italy & Spain': lambda x: x[grouped].sum(axis=1),
+        'Other': lambda x: x[other_cols].drop(columns=grouped).sum(axis=1)
+    }
+)[plot_cols].plot(
+    kind='area', figsize=(15, 5), 
+    title='7-day rolling average of new COVID-19 cases\n(source: ECDC)'
+)
+
+
+
+

+
+
+
+
+

Another way to visualize evolution over time is to look at the cumulative sum over time. Let’s plot the cumulative number of COVID-19 cases in China, Spain, Italy, the USA, Brazil, and India, using ax to create subplots as we did in the previous example.

+
+
fig, axes = plt.subplots(1, 3, figsize=(15, 3))
+
+cumulative_covid_cases = covid.groupby(
+    ['countriesAndTerritories', pd.Grouper(freq='1D')]
+).cases.sum().unstack(0).apply('cumsum')
+
+cumulative_covid_cases[['China']].plot(ax=axes[0], style='-.c')
+cumulative_covid_cases[['Italy', 'Spain']].plot(
+    ax=axes[1], style=['-', '--'], 
+    title='Cumulative COVID-19 Cases\n(source: ECDC)'
+)
+cumulative_covid_cases[['Brazil', 'India', 'USA']]\
+    .plot(ax=axes[2], style=['--', ':', '-'])
+
+
+
+

+
+
+
+
+

NOTE: we specified the line styles here so that the lines can be distinguished in the text as a black and white image.

+
+
+
+

Visualizing relationships between variables

+
+

Scatter plots

+

We make scatter plots to help visualize the relationship between two variables. Creating scatter plots requires we pass in kind='scatter' along with a column for the x-axis and a column for the y-axis:

+
+
fb.assign(
+    max_abs_change=fb.high - fb.low
+).plot(
+    kind='scatter', x='volume', y='max_abs_change',
+    title='Facebook Daily High - Low vs. Volume Traded'
+)
+
+
+
+

+
+
+
+
+

The relationship doesn’t seem to be linear, but we can try a log transform on the x-axis since the scales of the axes are very different. With pandas, we simply pass in logx=True:

+
+
fb.assign(
+    max_abs_change=fb.high - fb.low
+).plot(
+    kind='scatter', x='volume', y='max_abs_change',
+    title='Facebook Daily High - Low vs. log(Volume Traded)', 
+    logx=True
+)
+
+
+
+

+
+
+
+
+

With matplotlib, we could use plt.xscale('log') to do the same thing.

+
+
+

Adding Transparency to Plots with alpha

+

Sometimes our plots have many overlapping values, but this can be impossible to see. This can be addressed by increasing the transparency of what we are plotting using the alpha parameter. It is a float in the range [0, 1] where 0 is completely transparent and 1 is completely opaque. By default this is 1, so let’s put in a lower value and re-plot the scatter plot:

+
+
fb.assign(
+    max_abs_change=fb.high - fb.low
+).plot(
+    kind='scatter', x='volume', y='max_abs_change',
+    title='Facebook Daily High - Low vs. log(Volume Traded)',
+    logx=True, alpha=0.25
+)
+
+
+
+

+
+
+
+
+
+
+

Hexbins

+

In the previous example, we can start to see the overlaps, but it is still difficult. Hexbins are another plot type that divide up the plot into hexagons, which are shaded according to the density of points there. With pandas, this is the hexbin value for the kind argument. It may also be necessary to tweak the gridsize, which determines the number of hexagons along the y-axis:

+
+
fb.assign(
+    log_volume=np.log(fb.volume),
+    max_abs_change=fb.high - fb.low
+).plot(
+    kind='hexbin',
+    x='log_volume',
+    y='max_abs_change',
+    title='Facebook Daily High - Low vs. log(Volume Traded)',
+    colormap='gray_r',
+    gridsize=20, 
+    sharex=False # we have to pass this to see the x-axis
+)
+
+
+
+

+
+
+
+
+
+
+

Visualizing Correlations with Heatmaps

+

Pandas doesn’t offer heatmaps; however, if we are able to get our data into a matrix, we can use matshow() from matplotlib:

+
+
fig, ax = plt.subplots(figsize=(20, 10))
+
+# calculate the correlation matrix
+fb_corr = fb.assign(
+    log_volume=np.log(fb.volume),
+    max_abs_change=fb.high - fb.low
+).corr()
+
+# create the heatmap and colorbar
+im = ax.matshow(fb_corr, cmap='seismic')
+im.set_clim(-1, 1)
+fig.colorbar(im)
+
+# label the ticks with the column names
+labels = [col.lower() for col in fb_corr.columns]
+ax.set_xticks(ax.get_xticks()[1:-1]) # to handle bug in matplotlib
+ax.set_xticklabels(labels, rotation=45)
+ax.set_yticks(ax.get_yticks()[1:-1]) # to handle bug in matplotlib
+ax.set_yticklabels(labels)
+
+# include the value of the correlation coefficient in the boxes
+for (i, j), coef in np.ndenumerate(fb_corr):
+    ax.text(
+        i, j, fr'$\rho$ = {coef:.2f}', # raw (r), format (f) string
+        ha='center', va='center', 
+        color='white', fontsize=14
+    )
+
+
+
+

+
+
+
+
+

Accessing the values in the correlation matrix can be done with loc[]:

+
+
fb_corr.loc['max_abs_change', ['volume', 'log_volume']]
+
+
volume        0.642027
+log_volume    0.731542
+Name: max_abs_change, dtype: float64
+
+
+
+
+
+

Visualizing distributions

+
+

Histograms

+

With the pandas, making histograms is as easy as passing kind='hist' to the plot() method:

+
+
fb.volume.plot(
+    kind='hist', 
+    title='Histogram of Daily Volume Traded in Facebook Stock'
+)
+plt.xlabel('Volume traded') # label the x-axis (discussed in chapter 6)
+
+
Text(0.5, 0, 'Volume traded')
+
+
+
+
+

+
+
+
+
+

We can overlap histograms to compare distributions provided we use the alpha parameter. For example, let’s compare the usage and magnitude of the various measurement techniques (the magType column) in the data:

+
+
fig, axes = plt.subplots(figsize=(8, 5))
+
+for magtype in quakes.magType.unique():
+    data = quakes.query(f'magType == "{magtype}"').mag
+    if not data.empty:
+        data.plot(
+            kind='hist', ax=axes, alpha=0.4, 
+            label=magtype, legend=True,
+            title='Comparing histograms of earthquake magnitude by magType'
+        )
+
+plt.xlabel('magnitude') # label the x-axis (discussed in chapter 6)
+
+
Text(0.5, 0, 'magnitude')
+
+
+
+
+

+
+
+
+
+
+
+

Kernel Density Estimation (KDE)

+

We can pass kind='kde' for an estimate of the probability density function (PDF), which tells us the probability of getting a particular value:

+
+
fb.high.plot(
+    kind='kde', 
+    title='KDE of Daily High Price for Facebook Stock'
+)
+plt.xlabel('Price ($)') # label the x-axis (discussed in chapter 6)
+
+
Text(0.5, 0, 'Price ($)')
+
+
+
+
+

+
+
+
+
+
+
+

Adding to the result of plot()

+

The plot() method returns an Axes object. We can store this for additional customization of the plot, or we can pass this into another call to plot() as the ax argument to add to the original plot.

+

It can often be helpful to view the KDE superimposed on top of the histogram, which can be achieved with this strategy:

+
+
ax = fb.high.plot(kind='hist', density=True, alpha=0.5)
+fb.high.plot(
+    ax=ax, kind='kde', color='blue', 
+    title='Distribution of Facebook Stock\'s Daily High Price in 2018'
+)
+plt.xlabel('Price ($)') # label the x-axis (discussed in chapter 6)
+
+
Text(0.5, 0, 'Price ($)')
+
+
+
+
+

+
+
+
+
+
+
+

Plotting the ECDF

+

In some cases, we are more interested in the probability of getting less than or equal to that value (or greater than or equal), which we can see with the cumulative disribution function (CDF). Using the statsmodels package, we can estimate the CDF giving us the empirical cumulative distribution function (ECDF):

+
+
from statsmodels.distributions.empirical_distribution import ECDF
+
+ecdf = ECDF(quakes.query('magType == "ml"').mag)
+plt.plot(ecdf.x, ecdf.y)
+
+# axis labels (we will cover this in chapter 6)
+plt.xlabel('mag') # add x-axis label 
+plt.ylabel('cumulative probability') # add y-axis label
+
+# add title (we will cover this in chapter 6)
+plt.title('ECDF of earthquake magnitude with magType ml')
+
+
Text(0.5, 1.0, 'ECDF of earthquake magnitude with magType ml')
+
+
+
+
+

+
+
+
+
+

This ECDF tells us the probability of getting an earthquake with magnitude of 3 or less using the ml scale is 98%:

+
+
from statsmodels.distributions.empirical_distribution import ECDF
+
+ecdf = ECDF(quakes.query('magType == "ml"').mag)
+plt.plot(ecdf.x, ecdf.y)
+
+# formatting below will all be covered in chapter 6
+# axis labels
+plt.xlabel('mag') # add x-axis label 
+plt.ylabel('cumulative probability') # add y-axis label
+
+# add reference lines for interpreting the ECDF for mag <= 3 
+plt.plot(
+    [3, 3], [0, .98], '--k', 
+    [-1.5, 3], [0.98, 0.98], '--k', alpha=0.4
+)
+
+# set axis ranges
+plt.ylim(0, None)
+plt.xlim(-1.25, None)
+
+# add a title
+plt.title('P(mag <= 3) = 98%')
+
+
Text(0.5, 1.0, 'P(mag <= 3) = 98%')
+
+
+
+
+

+
+
+
+
+
+
+

Box plots

+

To make box plots with pandas, we pass kind='box' to the plot() method:

+
+
fb.iloc[:,:4].plot(kind='box', title='Facebook OHLC Prices Box Plot')
+plt.ylabel('price ($)') # label the y-axis (discussed in chapter 6)
+
+
Text(0, 0.5, 'price ($)')
+
+
+
+
+

+
+
+
+
+

If we pass in notch=True, we get a notched box plot. The notch represents a 95% confidence interval around the median, which can be helpful when comparing differences. For an introduction to interpreting a notched box plot, see this Google sites page and this Towards Data Science article.

+
+
fb.iloc[:,:4].plot(kind='box', title='Facebook OHLC Prices Box Plot', notch=True)
+plt.ylabel('price ($)') # label the y-axis (discussed in chapter 6)
+
+
Text(0, 0.5, 'price ($)')
+
+
+
+
+

+
+
+
+
+

This can also be combined with a call to groupby():

+
+
fb.assign(
+    volume_bin=pd.cut(fb.volume, 3, labels=['low', 'med', 'high'])
+).groupby('volume_bin').boxplot(
+    column=['open', 'high', 'low', 'close'],
+    layout=(1, 3), figsize=(12, 3)
+)
+plt.suptitle('Facebook OHLC Box Plots by Volume Traded', y=1.1)
+
+
Text(0.5, 1.1, 'Facebook OHLC Box Plots by Volume Traded')
+
+
+
+
+

+
+
+
+
+

We can use this to see the distribution of magnitudes across the different measurement methods for earthquakes:

+
+
quakes[['mag', 'magType']].groupby('magType').boxplot(
+    figsize=(15, 8), subplots=False
+)
+plt.title('Earthquake Magnitude Box Plots by magType')
+plt.ylabel('magnitude') # label the y-axis (discussed in chapter 6)
+
+
Text(0, 0.5, 'magnitude')
+
+
+
+
+

+
+
+
+
+
+
+
+

Counts and frequencies

+
+

Bar charts

+

Passing kind='barh' gives us horizontal bars while kind='bar' gives us vertical ones. Let’s use horizontal bars to look at the top 15 places for earthquakes in our data:

+
+
quakes.parsed_place.value_counts().iloc[14::-1,].plot(
+    kind='barh', figsize=(10, 5),
+    title='Top 15 Places for Earthquakes '
+          '(September 18, 2018 - October 13, 2018)'
+)
+plt.xlabel('earthquakes') # label the x-axis (discussed in chapter 6)
+
+
Text(0.5, 0, 'earthquakes')
+
+
+
+
+

+
+
+
+
+

We also have data on whether earthquakes were accompanied by tsunamis. Let’s see what the top places for tsunamis are:

+
+
quakes.groupby('parsed_place').tsunami.sum().sort_values().iloc[-10:,].plot(
+    kind='barh', figsize=(10, 5), 
+    title='Top 10 Places for Tsunamis '
+          '(September 18, 2018 - October 13, 2018)'
+)
+plt.xlabel('tsunamis') # label the x-axis (discussed in chapter 6)
+
+
Text(0.5, 0, 'tsunamis')
+
+
+
+
+

+
+
+
+
+

Seeing that Indonesia is the top place for tsunamis during the time period we are looking at, we may want to look how many earthquakes and tsunamis Indonesia gets on a daily basis. We could show this as a line plot or with bars; since we don’t want to interpolate, we will use bars here:

+
+
indonesia_quakes = quakes.query('parsed_place == "Indonesia"').assign(
+    time=lambda x: pd.to_datetime(x.time, unit='ms'),
+    earthquake=1
+).set_index('time').resample('1D').sum()
+
+# format the datetimes in the index for the x-axis
+indonesia_quakes.index = indonesia_quakes.index.strftime('%b\n%d')
+
+indonesia_quakes.plot(
+    y=['earthquake', 'tsunami'], kind='bar', figsize=(15, 3), 
+    rot=0, label=['earthquakes', 'tsunamis'], 
+    title='Earthquakes and Tsunamis in Indonesia '
+          '(September 18, 2018 - October 13, 2018)'
+)
+
+# label the axes (discussed in chapter 6)
+plt.xlabel('date')
+plt.ylabel('count')
+
+
C:\Users\gpower\AppData\Local\Temp\ipykernel_13112\3940988219.py:4: FutureWarning: The default value of numeric_only in DataFrameGroupBy.sum is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.
+  ).set_index('time').resample('1D').sum()
+
+
+
Text(0, 0.5, 'count')
+
+
+
+
+

+
+
+
+
+
+
+

Grouped Bars

+
+
quakes.groupby(['parsed_place', 'tsunami']).mag.count()\
+    .unstack().apply(lambda x: x / x.sum(), axis=1)\
+    .rename(columns={0: 'no', 1: 'yes'})\
+    .sort_values('yes', ascending=False)[7::-1]\
+    .plot.barh(
+        title='Frequency of a tsunami accompanying an earthquake'
+    )
+
+# move legend to the right of the plot
+plt.legend(title='tsunami?', bbox_to_anchor=(1, 0.65))
+
+# label the axes (discussed in chapter 6)
+plt.xlabel('percentage of earthquakes')
+plt.ylabel('')
+
+
Text(0, 0.5, '')
+
+
+
+
+

+
+
+
+
+

Using the kind arugment for vertical bars when the labels for each bar are shorter:

+
+
quakes.magType.value_counts().plot(
+    kind='bar', title='Earthquakes Recorded per magType', rot=0
+)
+
+# label the axes (discussed in chapter 6)
+plt.xlabel('magType')
+plt.ylabel('earthquakes')
+
+
Text(0, 0.5, 'earthquakes')
+
+
+
+
+

+
+
+
+
+
+
+

Stacked bars

+
+
pivot = quakes.assign(
+    mag_bin=lambda x: np.floor(x.mag)
+).pivot_table(
+    index='mag_bin', columns='magType', values='mag', aggfunc='count'
+)
+pivot.plot.bar(
+    stacked=True, rot=0, ylabel='earthquakes', 
+    title='Earthquakes by integer magnitude and magType'
+)
+
+
+
+

+
+
+
+
+
+

Normalized stacked bars

+

Plot the percentages to be better able to see the different magTypes.

+
+
normalized_pivot = pivot.fillna(0).apply(lambda x: x / x.sum(), axis=1)
+ax = normalized_pivot.plot.bar(
+    stacked=True, rot=0, figsize=(10, 5),
+    title='Percentage of earthquakes by integer magnitude for each magType'
+)
+ax.legend(bbox_to_anchor=(1, 0.8)) # move legend to the right of the plot
+plt.ylabel('percentage') # label the y-axis (discussed in chapter 6)
+
+
Text(0, 0.5, 'percentage')
+
+
+
+
+

+
+
+
+
+

We can also create horizontal stacked bars and do so using groupby() and unstack():

+
+
quakes.groupby(['parsed_place', 'tsunami']).mag.count()\
+    .unstack().apply(lambda x: x / x.sum(), axis=1)\
+    .rename(columns={0: 'no', 1: 'yes'})\
+    .sort_values('yes', ascending=False)[7::-1]\
+    .plot.barh(
+        title='Frequency of a tsunami accompanying an earthquake', 
+        stacked=True
+    )
+
+# move legend to the right of the plot
+plt.legend(title='tsunami?', bbox_to_anchor=(1, 0.65))
+
+# label the axes (discussed in chapter 6)
+plt.xlabel('percentage of earthquakes')
+plt.ylabel('')
+
+
Text(0, 0.5, '')
+
+
+
+
+

+
+
+
+
+ + +
+
+
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-10-output-1.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-10-output-1.png new file mode 100644 index 0000000..088c12d Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-10-output-1.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-11-output-1.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-11-output-1.png new file mode 100644 index 0000000..5a09157 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-11-output-1.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-12-output-1.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-12-output-1.png new file mode 100644 index 0000000..3be1a30 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-12-output-1.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-13-output-1.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-13-output-1.png new file mode 100644 index 0000000..9211d7a Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-13-output-1.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-14-output-1.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-14-output-1.png new file mode 100644 index 0000000..406a48c Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-14-output-1.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-15-output-1.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-15-output-1.png new file mode 100644 index 0000000..8a1823d Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-15-output-1.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-17-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-17-output-2.png new file mode 100644 index 0000000..74d070c Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-17-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-18-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-18-output-2.png new file mode 100644 index 0000000..5cd4a2f Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-18-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-19-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-19-output-2.png new file mode 100644 index 0000000..86c30ff Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-19-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-20-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-20-output-2.png new file mode 100644 index 0000000..6d915b0 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-20-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-21-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-21-output-2.png new file mode 100644 index 0000000..403420e Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-21-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-22-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-22-output-2.png new file mode 100644 index 0000000..e346349 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-22-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-23-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-23-output-2.png new file mode 100644 index 0000000..be34bac Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-23-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-24-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-24-output-2.png new file mode 100644 index 0000000..fa8414d Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-24-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-25-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-25-output-2.png new file mode 100644 index 0000000..d6aab6a Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-25-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-26-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-26-output-2.png new file mode 100644 index 0000000..9eebb46 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-26-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-27-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-27-output-2.png new file mode 100644 index 0000000..85c6289 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-27-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-28-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-28-output-2.png new file mode 100644 index 0000000..6cd91e5 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-28-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-29-output-3.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-29-output-3.png new file mode 100644 index 0000000..e8b8c9b Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-29-output-3.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-3-output-1.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-3-output-1.png new file mode 100644 index 0000000..48c5c99 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-3-output-1.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-30-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-30-output-2.png new file mode 100644 index 0000000..3c81963 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-30-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-31-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-31-output-2.png new file mode 100644 index 0000000..e171d60 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-31-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-32-output-1.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-32-output-1.png new file mode 100644 index 0000000..0d51802 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-32-output-1.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-33-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-33-output-2.png new file mode 100644 index 0000000..0ed7bee Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-33-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-34-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-34-output-2.png new file mode 100644 index 0000000..efd2a9c Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-34-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-4-output-1.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-4-output-1.png new file mode 100644 index 0000000..48c5c99 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-4-output-1.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-5-output-1.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-5-output-1.png new file mode 100644 index 0000000..56cc2e8 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-5-output-1.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-6-output-2.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-6-output-2.png new file mode 100644 index 0000000..d342a66 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-6-output-2.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-8-output-1.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-8-output-1.png new file mode 100644 index 0000000..41be4e5 Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-8-output-1.png differ diff --git a/docs/notebooks/plotting_with_pandas_files/figure-html/cell-9-output-1.png b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-9-output-1.png new file mode 100644 index 0000000..06e8bad Binary files /dev/null and b/docs/notebooks/plotting_with_pandas_files/figure-html/cell-9-output-1.png differ diff --git a/docs/notebooks/python_101.html b/docs/notebooks/python_101.html new file mode 100644 index 0000000..01842ac --- /dev/null +++ b/docs/notebooks/python_101.html @@ -0,0 +1,1231 @@ + + + + + + + + + +Python 101 + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+

Python 101

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Python 101

+

This is an optional notebook to get you up to speed with Python in case you are new to Python or need a refresher. The material here is a crash course in Python; I highly recommend the official Python tutorial for a deeper dive. Consider reading this page in the Python docs for background on Python and bookmarking the glossary.

+
+

Basic data types

+
+

Numbers

+

Numbers in Python can be represented as integers (e.g. 5) or floats (e.g. 5.0). We can perform operations on them:

+
+
5 + 6
+
+
11
+
+
+
+
2.5 / 3
+
+
0.8333333333333334
+
+
+
+
+

Booleans

+

We can check for equality giving us a Boolean:

+
+
5 == 6
+
+
False
+
+
+
+
5 < 6
+
+
True
+
+
+

These statements can be combined with logical operators: not, and, or

+
+
(5 < 6) and not (5 == 6)
+
+
True
+
+
+
+
False or True
+
+
True
+
+
+
+
True or False
+
+
True
+
+
+
+
+

Strings

+

Using strings, we can handle text in Python. These values must be surrounded in quotes — single ('...') is the standard, but double ("...") works as well:

+
+
'hello'
+
+
'hello'
+
+
+

We can also perform operations on strings. For example, we can see how long it is with len():

+
+
len('hello')
+
+
5
+
+
+

We can select parts of the string by specifying the index. Note that in Python the 1st character is at index 0:

+
+
'hello'[0]
+
+
'h'
+
+
+

We can concatentate strings with +:

+
+
'hello' + ' ' + 'world'
+
+
'hello world'
+
+
+

We can check if characters are in the string with the in operator:

+
+
'h' in 'hello'
+
+
True
+
+
+
+
+
+

Variables

+

Notice that just typing text causes an error. Errors in Python attempt to clue us in to what went wrong with our code. In this case, we have a NameError exception which tells us that 'hello' is not defined. This means that the Python interpreter looked for a variable named hello, but it didn’t find one.

+
+
hello
+
+
NameError: name 'hello' is not defined
+
+
+

Variables give us a way to store data types. We define a variable using the variable_name = value syntax:

+
+
x = 5
+y = 7
+x + y
+
+
12
+
+
+

The variable name cannot contain spaces; we usually use _ instead. The best variable names are descriptive ones:

+
+
book_title = 'Hands-On Data Analysis with Pandas'
+
+

Variables can be any data type. We can check which one it is with type(), which is a function (more on that later):

+
+
type(x)
+
+
int
+
+
+
+
type(book_title)
+
+
str
+
+
+

If we need to see the value of a variable, we can print it using the print() function:

+
+
print(book_title)
+
+
Hands-On Data Analysis with Pandas
+
+
+
+
+

Collections of Items

+
+

Lists

+

We can store a collection of items in a list:

+
+
['hello', ' ', 'world']
+
+
['hello', ' ', 'world']
+
+
+

The list can be stored in a variable. Note that the items in the list can be of different types:

+
+
my_list = ['hello', 3.8, True, 'Python']
+type(my_list)
+
+
list
+
+
+

We can see how many elements are in the list with len():

+
+
len(my_list)
+
+
4
+
+
+

We can also use the in operator to check if a value is in the list:

+
+
'world' in my_list
+
+
False
+
+
+

We can select items in the list just as we did with strings, by providing the index to select:

+
+
my_list[1]
+
+
3.8
+
+
+

Python also allows us to use negative values, so we can easily select the last one:

+
+
my_list[-1]
+
+
'Python'
+
+
+

Another powerful feature of lists (and strings) is slicing. We can grab the middle 2 elements in the list:

+
+
my_list[1:3]
+
+
[3.8, True]
+
+
+

… or every other one:

+
+
my_list[::2]
+
+
['hello', True]
+
+
+

We can even select the list in reverse:

+
+
my_list[::-1]
+
+
['Python', True, 3.8, 'hello']
+
+
+

Note: This syntax is [start:stop:step] where the selection is inclusive of the start index, but exclusive of the stop index. If start isn’t provided, 0 is used. If stop isn’t provided, the number of elements is used (4, in our case); this works because the stop is exclusive. If step isn’t provided, it is 1.

+

We can use the join() method on a string object to concatenate all the items of a list into single string. The string we call the join() method on will be used as the separator, here we separate with a pipe (|):

+
+
'|'.join(['x', 'y', 'z'])
+
+
'x|y|z'
+
+
+
+
+

Tuples

+

Tuples are similar to lists; however, they can’t be modified after creation i.e. they are immutable. Instead of square brackets, we use parenthesis to create tuples:

+
+
my_tuple = ('a', 5)
+type(my_tuple)
+
+
tuple
+
+
+
+
my_tuple[0]
+
+
'a'
+
+
+

Immutable objects can’t be modified:

+
+
my_tuple[0] = 'b'
+
+
TypeError: 'tuple' object does not support item assignment
+
+
+
+
+

Dictionaries

+

We can store mappings of key-value pairs using dictionaries:

+
+
shopping_list = {
+    'veggies': ['spinach', 'kale', 'beets'],
+    'fruits': 'bananas',
+    'meat': 0    
+}
+type(shopping_list)
+
+
dict
+
+
+

To access the values associated with a specific key, we use the square bracket notation again:

+
+
shopping_list['veggies']
+
+
['spinach', 'kale', 'beets']
+
+
+

We can extract all of the keys with keys():

+
+
shopping_list.keys()
+
+
dict_keys(['veggies', 'fruits', 'meat'])
+
+
+

We can extract all of the values with values():

+
+
shopping_list.values()
+
+
dict_values([['spinach', 'kale', 'beets'], 'bananas', 0])
+
+
+

Finally, we can call items() to get back pairs of (key, value) pairs:

+
+
shopping_list.items()
+
+
dict_items([('veggies', ['spinach', 'kale', 'beets']), ('fruits', 'bananas'), ('meat', 0)])
+
+
+
+
+

Sets

+

A set is a collection of unique items; a common use is to remove duplicates from a list. These are written with curly braces also, but notice there is no key-value mapping:

+
+
my_set = {1, 1, 2, 'a'}
+type(my_set)
+
+
set
+
+
+

How many items are in this set?

+
+
len(my_set)
+
+
3
+
+
+

We put in 4 items but the set only has 3 because duplicates are removed:

+
+
my_set
+
+
{1, 2, 'a'}
+
+
+

We can check if a value is in the set:

+
+
2 in my_set
+
+
True
+
+
+
+
+
+

Functions

+

We can define functions to package up our code for reuse. We have already seen some functions: len(), type(), and print(). They are all functions that take arguments. Note that functions don’t need to accept arguments, in which case they are called without passing in anything (e.g. print() versus print(my_string)).

+

Aside: we can also create lists, sets, dictionaries, and tuples with functions: list(), set(), dict(), and tuple()

+
+

Defining functions

+

We use the def keyword to define functions. Let’s create a function called add() with 2 parameters, x and y, which will be the names the code in the function will use to refer to the arguments we pass in when calling it:

+
+
def add(x, y):
+    """This is a docstring. It is used to explain how the code works and is optional (but encouraged)."""
+    # this is a comment; it allows us to annotate the code
+    print('Performing addition')
+    return x + y
+
+

Once we run the code above, our function is ready to use:

+
+
type(add)
+
+
function
+
+
+

Let’s add some numbers:

+
+
add(1, 2)
+
+
Performing addition
+
+
+
3
+
+
+
+
+

Return values

+

We can store the result in a variable for later:

+
+
result = add(1, 2)
+
+
Performing addition
+
+
+

Notice the print statement wasn’t captured in result. This variable will only have what the function returns. This is what the return line in the function definition did:

+
+
result
+
+
3
+
+
+

Note that functions don’t have to return anything. Consider print():

+
+
print_result = print('hello world')
+
+
hello world
+
+
+

If we take a look at what we got back, we see it is a NoneType object:

+
+
type(print_result)
+
+
NoneType
+
+
+

In Python, the value None represents null values. We can check if our variable is None:

+
+
print_result is None
+
+
True
+
+
+

Warning: make sure to use comparison operators (e.g. >, >=, <, <=, ==, !=) to compare to values other than None.

+
+
+

Function arguments

+

Note that function arguments can be anything, even other functions. We will see several examples of this in the text.

+

The function we defined requires arguments. If we don’t provide them all, it will cause an error:

+
+
add(1)
+
+
TypeError: add() missing 1 required positional argument: 'y'
+
+
+

We can use help() to check what arguments the function needs (notice the docstring ends up here):

+
+
help(add)
+
+
Help on function add in module __main__:
+
+add(x, y)
+    This is a docstring. It is used to explain how the code works and is optional (but encouraged).
+
+
+
+

We will also get errors if we pass in data types that add() can’t work with:

+
+
add(set(), set())
+
+
Performing addition
+
+
+
TypeError: unsupported operand type(s) for +: 'set' and 'set'
+
+
+

We will discuss error handling in the text.

+
+
+
+

Control Flow Statements

+

Sometimes we want to vary the path the code takes based on some criteria. For this we have if, elif, and else. We can use if on its own:

+
+
def make_positive(x):
+    """Returns a positive x"""
+    if x < 0:
+        x *= -1
+    return x
+
+

Calling this function with negative input causes the code under the if statement to run:

+
+
make_positive(-1)
+
+
1
+
+
+

Calling this function with positive input skips the code under the if statement, keeping the number positive:

+
+
make_positive(2)
+
+
2
+
+
+

Sometimes we need an else statement as well:

+
+
def add_or_subtract(operation, x, y):
+    if operation == 'add':
+        return x + y
+    else:
+        return x - y
+
+

This triggers the code under the if statement:

+
+
add_or_subtract('add', 1, 2)
+
+
3
+
+
+

Since the Boolean check in the if statement was False, this triggers the code under the else statement:

+
+
add_or_subtract('subtract', 1, 2)
+
+
-1
+
+
+

For more complicated logic, we can also use elif. We can have any number of elif statements. Optionally, we can include else.

+
+
def calculate(operation, x, y):
+    if operation == 'add':
+        return x + y
+    elif operation == 'subtract':
+        return x - y
+    elif operation == 'multiply':
+        return x * y
+    elif operation == 'division':
+        return x / y
+    else:
+        print("This case hasn't been handled")
+
+

The code keeps checking the conditions in the if statements from top to bottom until it finds multiply:

+
+
calculate('multiply', 3, 4)
+
+
12
+
+
+

The code keeps checking the conditions in the if statements from top to bottom until it hits the else statement:

+
+
calculate('power', 3, 4)
+
+
This case hasn't been handled
+
+
+
+
+

Loops

+
+

while loops

+

With while loops, we can keep running code until some stopping condition is met:

+
+
done = False
+value = 2
+while not done:
+    print('Still going...', value)
+    value *= 2
+    if value > 10:
+        done = True
+
+
Still going... 2
+Still going... 4
+Still going... 8
+
+
+

Note this can also be written as, by moving the condition to the while statement:

+
+
value = 2
+while value < 10:
+    print('Still going...', value)
+    value *= 2
+
+
Still going... 2
+Still going... 4
+Still going... 8
+
+
+
+
+

for loops

+

With for loops, we can run our code for each element in a collection:

+
+
for i in range(5):
+    print(i)
+
+
0
+1
+2
+3
+4
+
+
+

We can use for loops with lists, tuples, sets, and dictionaries as well:

+
+
for element in my_list:
+    print(element)
+
+
hello
+3.8
+True
+Python
+
+
+
+
for key, value in shopping_list.items():
+    print('For', key, 'we need to buy', value)
+
+
For veggies we need to buy ['spinach', 'kale', 'beets']
+For fruits we need to buy bananas
+For meat we need to buy 0
+
+
+

With for loops, we don’t have to worry about checking if we have reached the stopping condition. Conversely, while loops can cause infinite loops if we don’t remember to update variables.

+
+
+
+

Imports

+

We have been working with the portion of Python that is available without importing additional functionality. The Python standard library that comes with the install of Python is broken up into several modules, but we often only need a few. We can import whatever we need: a module in the standard library, a 3rd-party library, or code that we wrote. This is done with an import statement:

+
+
import math
+
+print(math.pi)
+
+
3.141592653589793
+
+
+

If we only need a small piece from that module, we can do the following instead:

+
+
from math import pi
+
+print(pi)
+
+
3.141592653589793
+
+
+

Warning: anything you import is added to the namespace, so if you create a new variable/function/etc. with the same name it will overwrite the previous value. For this reason, we have to be careful with variable names e.g. if you name something sum, you won’t be able to add using the sum() built-in function anymore. Using notebooks or an IDE will help you avoid these issues with syntax highlighting.

+
+
+

Installing 3rd-party Packages

+

We can use pip or conda to install packages, depending on how we created our virtual environment. We will walk through the commands to create virtual environments with conda. The environment MUST be activated before installing the packages for this text; otherwise, it’s possible they interfere with other projects on your machine or vice versa.

+

To install a package, we can use conda install <package_name> to download a package from the default conda channel. Optionally, we can provide a specific version to install conda install pandas==0.23.4. Even further, can define which channel that we install a package from for example we can install a package from the conda-forge channel by with conda install -c conda-forge pandas=0.23.4. Without that specification, we will get the most stable version. When we have many packages to install we will typically use a environment.yml or requirements.txt file: conda env update -f environment.yml from within your active environment or conda env update -n ENVNAME -f environment.yml if you are updating an update you are not actively in.

+

Note: running conda env export ENVNAME > environment.yml will send the list of platform-specific packages installed in the activate environment and their respective versions to the environment.yml file.

+
+
+

Classes

+

So far we have used Python as a functional programming language, but we also have the option to use it for object-oriented programming. You can think of a class as a way to group similar functionality together. Let’s create a calculator class which can handle mathematical operations for us. For this, we use the class keyword and define methods for taking actions on the calculator. These methods are functions that take self as the first argument. When calling them, we don’t pass in anything for that argument (example after this):

+
+
class Calculator:
+    """This is the class docstring."""
+    
+    def __init__(self):
+        """This is a method and it is called when we create an object of type `Calculator`."""
+        self.on = False
+        
+    def turn_on(self):
+        """This method turns on the calculator."""
+        self.on = True
+    
+    def add(self, x, y):
+        """Perform addition if calculator is on"""
+        if self.on:
+            return x + y
+        else:
+            print('the calculator is not on')
+
+

In order to use the calculator, we need to instantiate an instance or object of type Calculator. Since the __init__() method has no parameters other than self, we don’t need to provide anything:

+
+
my_calculator = Calculator()
+
+

Let’s try to add some numbers:

+
+
my_calculator.add(1, 2)
+
+
the calculator is not on
+
+
+

Oops!! The calculator is not on. Let’s turn it on:

+
+
my_calculator.turn_on()
+
+

Let’s try again:

+
+
my_calculator.add(1, 2)
+
+
3
+
+
+

We can access attributes on object with dot notation. In this example, the only attribute is on, and it is set in the __init__() method:

+
+
my_calculator.on
+
+
True
+
+
+

Note that we can also update attributes:

+
+
my_calculator.on = False
+my_calculator.add(1, 2)
+
+
the calculator is not on
+
+
+

Finally, we can use help() to get more information on the object:

+
+
help(my_calculator)
+
+
Help on Calculator in module __main__ object:
+
+class Calculator(builtins.object)
+ |  This is the class docstring.
+ |  
+ |  Methods defined here:
+ |  
+ |  __init__(self)
+ |      This is a method and it is called when we create an object of type `Calculator`.
+ |  
+ |  add(self, x, y)
+ |      Perform addition if calculator is on
+ |  
+ |  turn_on(self)
+ |      This method turns on the calculator.
+ |  
+ |  ----------------------------------------------------------------------
+ |  Data descriptors defined here:
+ |  
+ |  __dict__
+ |      dictionary for instance variables (if defined)
+ |  
+ |  __weakref__
+ |      list of weak references to the object (if defined)
+
+
+
+

… and also for a method:

+
+
help(my_calculator.add)
+
+
Help on method add in module __main__:
+
+add(x, y) method of __main__.Calculator instance
+    Perform addition if calculator is on
+
+
+
+
+
+

Next Steps

+

This was a crash course in Python. This isn’t an exhaustive list of all of the features available to you.

+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/docs/notebooks/python_errors.html b/docs/notebooks/python_errors.html new file mode 100644 index 0000000..aca64e5 --- /dev/null +++ b/docs/notebooks/python_errors.html @@ -0,0 +1,706 @@ + + + + + + + + + +Python Errors + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+

Python Errors

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Python Errors

+

When an error arises, there will be an error message with the type of error and the line the error occured on. This notebook goes over how to handle the common types of errors and exceptions in Python.

+

I recommend looking at the Python tutorial page for more information on errors. Searching for the error message directly on Google can help the debugging process if there is an error not discussed in this page.

+
+

Syntax Error

+

A SyntaxError occurs when the syntax of your code is incorrect.

+
+
if True 
+print("Hello World")
+
+
SyntaxError: expected ':' (975521850.py, line 1)
+
+
+

A colon is expected after the if statement, which arises the syntax error. The error goes away after adding the colon.

+
+
if True:
+    print("Hello World")
+
+
Hello World
+
+
+
+
+

Name Error

+

A NameError occurs when a variable, function, or module used does not exist. When this happens, it is usually because of a spelling error.

+
+
add
+
+
NameError: name 'add' is not defined
+
+
+
+
string(9)
+
+
NameError: name 'string' is not defined
+
+
+
+
+

Type Error

+

A TypeError occurs when you input an incorrect data type for an operation or function.

+
+
"abc" + 9
+
+
TypeError: can only concatenate str (not "int") to str
+
+
+

In python, you cannot add strings to integers. You can add, however, an integer to an integer or a string to a string with a +.

+
+
9 + 9
+
+
18
+
+
+
+
"abc" + "def"
+
+
'abcdef'
+
+
+
+
+

Zero Division Error

+

A ZeroDivisionError occurs when you try to divide by zero. To fix this, recheck your computation.

+
+
2 / (9 * 0)
+
+
ZeroDivisionError: division by zero
+
+
+
+
#code corrected to no longer divide by zero
+(2 / 9) * 0
+
+
0.0
+
+
+
+
+

Value Error

+

A ValueError occurs when an input for a function is the correct data type but is invalid in regards to the domain of the function. This is most common with mathematical operations.

+
+
import math
+
+math.sqrt(-10)
+
+
ValueError: math domain error
+
+
+

In the example above, you must input a positive number into the sqrt() function. The negative number is still an integer, but it is not in the function’s domain.

+
+
math.sqrt(10)
+
+
3.1622776601683795
+
+
+
+
+

Index Error

+

An IndexError occurs when you try to access an item in a list with an index out of bounds.

+
+
list = [1,2,3,4,5]
+list[5]
+
+
IndexError: list index out of range
+
+
+

The range of a list is [0, n-1], where “n” is the length of the list. So, the list [1,2,3,4,5] has index elements in the range 0-4.

+
+
list[4]
+
+
5
+
+
+
+
+

Module Not Found Error

+

A ModuleNotFoundError occurs when you try to import a module that does not exist. It is a type of ImportError. To fix this error, check if you have installed the module in your python environment from the terminal command-line.

+
+
import pillow
+
+
ModuleNotFoundError: No module named 'pillow'
+
+
+
+
+

Catching Exceptions with Try Statements

+

You can use a try statement to catch errors. A try clause includes the code you want to run that might cause an error. If no error occurs, the try clause runs successfully. If an error does occur, the except clause runs after the line in the try clause that caused an error.

+
+
try:
+    "abc" + 9
+    print("Success")
+except:
+    print("Failure to execute")
+
+
Failure to execute
+
+
+

The except clause above can catch any type of error. However, an except clause can also catch a specific type of error. There can be mulptile except clauses in a try statement to catch the different types of errors.

+
+
try:
+    hello
+    "abc" + 9
+    print("Success")
+except TypeError:
+    print("TypeError failure to execute")
+except NameError:
+    print("NameError failure to execute")
+
+
NameError failure to execute
+
+
+
+
try:
+    list = [1,2,3,4,5]
+    list[5]
+    print("Success")
+except TypeError:
+    print("TypeError failure to execute")
+except NameError:
+    print("NameError failure to execute")
+except IndexError:
+    print("IndexError failure to execute")
+
+
IndexError failure to execute
+
+
+
+
+

Next Steps

+

These are not all the errors that might come up in your coding. If another type of error occurs, you can search the error type on Google to learn more about what has caused it. As always, remember to look at the line resulting in the error for hints on what could have gone wrong!

+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/docs/notebooks/reading_local_files.html b/docs/notebooks/reading_local_files.html new file mode 100644 index 0000000..ef94199 --- /dev/null +++ b/docs/notebooks/reading_local_files.html @@ -0,0 +1,1026 @@ + + + + + + + + + +An Introduction to Jupyter Notebooks + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+

An Introduction to Jupyter Notebooks

+
+ + + +
+ + + + +
+ + + +
+ + +
+

An Introduction to Jupyter Notebooks

+

Jupyter Notebooks are a file format (*.ipynb) that you can execute and explain your code in a step-wise format. > Jupyter Notebooks supports not only code execution in Python, but over 40 languages including R, Lua, Rust, and Julia with numerous kernels.

+

We can write in Markdown to write text with some level of control over your formatting. - Here’s a Link to Basic Markdown - Here’s a link to Markdown’s Extended Syntax

+

Topics We Will Cover - Importing different files and filetypes with pandas - Basic Statistical Analysis of tabular data with pandas and numpy - Creating Charts with python packages from the Matplotlib, Plotly, or HoloViz Ecosystem - Evaluate the potential usecases for each visualization package

+

EvidenceOfLearning
This is you, enjoying the learning process.

+

Step 1: Import pandas into your python program.

+
+
import pandas as pd
+
+# This will import the pandas and numpy packages into your Python program.
+
+df_json = pd.read_json('../data/food-waste-pilot/food-waste-pilot.json')
+df_csv = pd.read_csv('../data/food-waste-pilot/food-waste-pilot.csv')
+df_xlsx = pd.read_excel('../data/food-waste-pilot/food-waste-pilot.xlsx')
+
+
+
df_csv.shape
+
+
(152, 3)
+
+
+
+
df_csv.head() # Grabs the top 5 items in your Dataframe by default.
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Collection DateFood Waste CollectedEstimated Earned Compost Created
02022-02-25250.825
12022-03-02298.830
22022-03-21601.260
32022-03-28857.286
42022-03-30610.861
+ +
+
+
+
+
df_csv.tail() # Grabs the bottom 5 items in your Dataframe by default.
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Collection DateFood Waste CollectedEstimated Earned Compost Created
1472022-10-12385.839
1482022-10-28713.671
1492022-10-31953.495
1502022-12-14694.469
1512023-01-06968.697
+ +
+
+
+
+
df_csv.columns
+
+
Index(['Collection Date', 'Food Waste Collected',
+       'Estimated Earned Compost Created'],
+      dtype='object')
+
+
+
+
df_csv.dtypes # Returns the data types of your columns.
+
+
Collection Date                      object
+Food Waste Collected                float64
+Estimated Earned Compost Created      int64
+dtype: object
+
+
+
+
df_csv.describe()
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Food Waste CollectedEstimated Earned Compost Created
count152.000000152.000000
mean526.87368452.611842
std197.83807519.787631
min0.0000000.000000
25%398.05000039.750000
50%531.50000053.000000
75%658.90000066.000000
max1065.800000107.000000
+ +
+
+
+
+
df_csv.info() # Returns index, column names, a count of Non-Null values, and data types.
+
+
<class 'pandas.core.frame.DataFrame'>
+RangeIndex: 152 entries, 0 to 151
+Data columns (total 3 columns):
+ #   Column                            Non-Null Count  Dtype  
+---  ------                            --------------  -----  
+ 0   Collection Date                   152 non-null    object 
+ 1   Food Waste Collected              152 non-null    float64
+ 2   Estimated Earned Compost Created  152 non-null    int64  
+dtypes: float64(1), int64(1), object(1)
+memory usage: 3.7+ KB
+
+
+

There are multiple methods to do type conversion using pandas as well.

+
+
# Oh no, we can see that our Collection Date is not the data type that we want, we need to convert it to a date value.
+
+df_csv['Collection Date'] = pd.to_datetime(df_csv['Collection Date'])
+
+
+
df_csv.info()
+
+
<class 'pandas.core.frame.DataFrame'>
+RangeIndex: 152 entries, 0 to 151
+Data columns (total 3 columns):
+ #   Column                            Non-Null Count  Dtype         
+---  ------                            --------------  -----         
+ 0   Collection Date                   152 non-null    datetime64[ns]
+ 1   Food Waste Collected              152 non-null    float64       
+ 2   Estimated Earned Compost Created  152 non-null    int64         
+dtypes: datetime64[ns](1), float64(1), int64(1)
+memory usage: 3.7 KB
+
+
+
+
# An alternative way to do this date conversion:
+
+df_csv['Collection Date'] = df_csv['Collection Date'].apply(pd.to_datetime)
+
+
+
# astype() is more generic method to convert data types
+
+df_csv['Collection Date'] = df_csv['Collection Date'].astype('datetime64[ns]')
+
+
+
df_csv.dtypes
+
+
Collection Date                     datetime64[ns]
+Food Waste Collected                       float64
+Estimated Earned Compost Created             int64
+dtype: object
+
+
+
+
# Now that we have converted our Collection Date column to a datetime data type, we can use the dt.day_name() method to create a new column that contains the day of the week.
+
+df_csv['Day of Week'] = df_csv['Collection Date'].dt.day_name()
+
+
+
# What if we want to know the date that we collected the most food waste?
+
+df_csv.loc[
+    df_csv['Food Waste Collected'].idxmax(),
+    ['Collection Date']
+]
+
+
Collection Date    2022-08-10 00:00:00
+Name: 20, dtype: object
+
+
+
+
# If you wanted to see our top 10 collection dates, you could do this:
+
+df_csv.nlargest(10,'Food Waste Collected')
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Collection DateFood Waste CollectedEstimated Earned Compost CreatedDay of Week
202022-08-101065.8107Wednesday
1242022-12-27987.499Tuesday
482022-09-12977.898Monday
1512023-01-06968.697Friday
1492022-10-31953.495Monday
32022-03-28857.286Monday
1232022-11-28844.484Monday
572022-12-05834.483Monday
912022-12-30815.482Friday
1372022-07-18807.881Monday
+ +
+
+
+
+
df_csv.nsmallest(10,'Food Waste Collected')
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Collection DateFood Waste CollectedEstimated Earned Compost CreatedDay of Week
532022-11-110.00Friday
952023-01-160.00Monday
1142022-09-050.00Monday
972022-02-0959.06Wednesday
362022-02-16102.810Wednesday
632022-02-21183.818Monday
612022-02-11197.020Friday
392022-04-15200.820Friday
622022-02-18202.820Friday
292022-12-10205.821Saturday
+ +
+
+
+
+
df_csv.plot()
+
+
+
+

+
+
+
+
+
+

You have to make sure that pandas parses your dates

+
+
df_csv_parsed_dates = pd.read_csv('../data/food-waste-pilot/food-waste-pilot.csv', parse_dates=True, index_col="Collection Date")
+
+
+
df_csv_parsed_dates.plot()
+
+
+
+

+
+
+
+
+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/docs/notebooks/reading_local_files_files/figure-html/cell-19-output-1.png b/docs/notebooks/reading_local_files_files/figure-html/cell-19-output-1.png new file mode 100644 index 0000000..0196671 Binary files /dev/null and b/docs/notebooks/reading_local_files_files/figure-html/cell-19-output-1.png differ diff --git a/docs/notebooks/reading_local_files_files/figure-html/cell-21-output-1.png b/docs/notebooks/reading_local_files_files/figure-html/cell-21-output-1.png new file mode 100644 index 0000000..9f287a7 Binary files /dev/null and b/docs/notebooks/reading_local_files_files/figure-html/cell-21-output-1.png differ diff --git a/docs/notebooks/wide_vs_long.html b/docs/notebooks/wide_vs_long.html new file mode 100644 index 0000000..352de6f --- /dev/null +++ b/docs/notebooks/wide_vs_long.html @@ -0,0 +1,911 @@ + + + + + + + + + +Wide vs. Long Format Data + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+

Wide vs. Long Format Data

+
+ + + +
+ + + + +
+ + + +
+ + +
+

Wide vs. Long Format Data

+
+

About the data

+

In this notebook, we will be using daily temperature data from the National Centers for Environmental Information (NCEI) API. We will use the Global Historical Climatology Network - Daily (GHCND) dataset for the Boonton 1 station (GHCND:USC00280907); see the documentation here.

+

Note: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.

+
+
+

Setup

+
+
import matplotlib.pyplot as plt
+import pandas as pd
+
+wide_df = pd.read_csv('../data/wide_data.csv', parse_dates=['date'])
+long_df = pd.read_csv(
+    '../data/long_data.csv', 
+    usecols=['date', 'datatype', 'value'], 
+    parse_dates=['date']
+)[['date', 'datatype', 'value']] # sort columns
+
+
+
+

Wide format

+

Our variables each have their own column:

+
+
wide_df.head(6)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
dateTMAXTMINTOBS
02018-10-0121.18.913.9
12018-10-0223.913.917.2
22018-10-0325.015.616.1
32018-10-0422.811.711.7
42018-10-0523.311.718.9
52018-10-0620.013.316.1
+ +
+
+
+

Describing all the columns is easy:

+
+
wide_df.describe(include='all')
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
dateTMAXTMINTOBS
count3131.00000031.00000031.000000
mean2018-10-16 00:00:0016.8290327.56129010.022581
min2018-10-01 00:00:007.800000-1.100000-1.100000
25%2018-10-08 12:00:0012.7500002.5000005.550000
50%2018-10-16 00:00:0016.1000006.7000008.300000
75%2018-10-23 12:00:0021.95000013.60000016.100000
max2018-10-31 00:00:0026.70000017.80000021.700000
stdNaN5.7149626.5132526.596550
+ +
+
+
+

It’s easy to graph with pandas:

+
+
wide_df.plot(
+    x='date', y=['TMAX', 'TMIN', 'TOBS'], figsize=(15, 5), 
+    title='Temperature in NYC in October 2018'
+).set_ylabel('Temperature in Celsius')
+plt.show()
+
+
+
+

+
+
+
+
+
+
+

Long format

+

Our variable names are now in the datatype column and their values are in the value column. We now have 3 rows for each date, since we have 3 different datatypes:

+
+
long_df.head(6)
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypevalue
02018-10-01TMAX21.1
12018-10-01TMIN8.9
22018-10-01TOBS13.9
32018-10-02TMAX23.9
42018-10-02TMIN13.9
52018-10-02TOBS17.2
+ +
+
+
+

Since we have many rows for the same date, using describe() is not that helpful:

+
+
long_df.describe(include='all')
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
datedatatypevalue
count939393.000000
uniqueNaN3NaN
topNaNTMAXNaN
freqNaN31NaN
mean2018-10-16 00:00:00NaN11.470968
min2018-10-01 00:00:00NaN-1.100000
25%2018-10-08 00:00:00NaN6.700000
50%2018-10-16 00:00:00NaN11.700000
75%2018-10-24 00:00:00NaN17.200000
max2018-10-31 00:00:00NaN26.700000
stdNaNNaN7.362354
+ +
+
+
+

Plotting long format data in pandas can be rather tricky. Instead we use seaborn:

+
+
import seaborn as sns
+
+sns.set(rc={'figure.figsize': (15, 5)}, style='white')
+
+ax = sns.lineplot(
+    data=long_df, x='date', y='value', hue='datatype'
+)
+ax.set_ylabel('Temperature in Celsius')
+ax.set_title('Temperature in NYC in October 2018')
+plt.show()
+
+
+
+

+
+
+
+
+

With long data and seaborn, we can easily facet our plots:

+
+
sns.set(
+    rc={'figure.figsize': (20, 10)}, style='white', font_scale=2
+)
+
+g = sns.FacetGrid(long_df, col='datatype', height=10)
+g = g.map(plt.plot, 'date', 'value')
+g.set_titles(size=25)
+g.set_xticklabels(rotation=45)
+plt.show()
+
+
+
+

+
+
+
+
+ + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/docs/notebooks/wide_vs_long_files/figure-html/cell-5-output-1.png b/docs/notebooks/wide_vs_long_files/figure-html/cell-5-output-1.png new file mode 100644 index 0000000..75fc111 Binary files /dev/null and b/docs/notebooks/wide_vs_long_files/figure-html/cell-5-output-1.png differ diff --git a/docs/notebooks/wide_vs_long_files/figure-html/cell-8-output-1.png b/docs/notebooks/wide_vs_long_files/figure-html/cell-8-output-1.png new file mode 100644 index 0000000..79b132a Binary files /dev/null and b/docs/notebooks/wide_vs_long_files/figure-html/cell-8-output-1.png differ diff --git a/docs/notebooks/wide_vs_long_files/figure-html/cell-9-output-1.png b/docs/notebooks/wide_vs_long_files/figure-html/cell-9-output-1.png new file mode 100644 index 0000000..87286f7 Binary files /dev/null and b/docs/notebooks/wide_vs_long_files/figure-html/cell-9-output-1.png differ diff --git a/docs/search.json b/docs/search.json new file mode 100644 index 0000000..c09e61c --- /dev/null +++ b/docs/search.json @@ -0,0 +1,527 @@ +[ + { + "objectID": "notebooks/wide_vs_long.html", + "href": "notebooks/wide_vs_long.html", + "title": "Wide vs. Long Format Data", + "section": "", + "text": "In this notebook, we will be using daily temperature data from the National Centers for Environmental Information (NCEI) API. We will use the Global Historical Climatology Network - Daily (GHCND) dataset for the Boonton 1 station (GHCND:USC00280907); see the documentation here.\nNote: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.\n\n\n\n\nimport matplotlib.pyplot as plt\nimport pandas as pd\n\nwide_df = pd.read_csv('../data/wide_data.csv', parse_dates=['date'])\nlong_df = pd.read_csv(\n '../data/long_data.csv', \n usecols=['date', 'datatype', 'value'], \n parse_dates=['date']\n)[['date', 'datatype', 'value']] # sort columns\n\n\n\n\nOur variables each have their own column:\n\nwide_df.head(6)\n\n\n\n\n\n\n\n\ndate\nTMAX\nTMIN\nTOBS\n\n\n\n\n0\n2018-10-01\n21.1\n8.9\n13.9\n\n\n1\n2018-10-02\n23.9\n13.9\n17.2\n\n\n2\n2018-10-03\n25.0\n15.6\n16.1\n\n\n3\n2018-10-04\n22.8\n11.7\n11.7\n\n\n4\n2018-10-05\n23.3\n11.7\n18.9\n\n\n5\n2018-10-06\n20.0\n13.3\n16.1\n\n\n\n\n\n\n\nDescribing all the columns is easy:\n\nwide_df.describe(include='all')\n\n\n\n\n\n\n\n\ndate\nTMAX\nTMIN\nTOBS\n\n\n\n\ncount\n31\n31.000000\n31.000000\n31.000000\n\n\nmean\n2018-10-16 00:00:00\n16.829032\n7.561290\n10.022581\n\n\nmin\n2018-10-01 00:00:00\n7.800000\n-1.100000\n-1.100000\n\n\n25%\n2018-10-08 12:00:00\n12.750000\n2.500000\n5.550000\n\n\n50%\n2018-10-16 00:00:00\n16.100000\n6.700000\n8.300000\n\n\n75%\n2018-10-23 12:00:00\n21.950000\n13.600000\n16.100000\n\n\nmax\n2018-10-31 00:00:00\n26.700000\n17.800000\n21.700000\n\n\nstd\nNaN\n5.714962\n6.513252\n6.596550\n\n\n\n\n\n\n\nIt’s easy to graph with pandas:\n\nwide_df.plot(\n x='date', y=['TMAX', 'TMIN', 'TOBS'], figsize=(15, 5), \n title='Temperature in NYC in October 2018'\n).set_ylabel('Temperature in Celsius')\nplt.show()\n\n\n\n\n\n\n\n\n\n\n\nOur variable names are now in the datatype column and their values are in the value column. We now have 3 rows for each date, since we have 3 different datatypes:\n\nlong_df.head(6)\n\n\n\n\n\n\n\n\ndate\ndatatype\nvalue\n\n\n\n\n0\n2018-10-01\nTMAX\n21.1\n\n\n1\n2018-10-01\nTMIN\n8.9\n\n\n2\n2018-10-01\nTOBS\n13.9\n\n\n3\n2018-10-02\nTMAX\n23.9\n\n\n4\n2018-10-02\nTMIN\n13.9\n\n\n5\n2018-10-02\nTOBS\n17.2\n\n\n\n\n\n\n\nSince we have many rows for the same date, using describe() is not that helpful:\n\nlong_df.describe(include='all')\n\n\n\n\n\n\n\n\ndate\ndatatype\nvalue\n\n\n\n\ncount\n93\n93\n93.000000\n\n\nunique\nNaN\n3\nNaN\n\n\ntop\nNaN\nTMAX\nNaN\n\n\nfreq\nNaN\n31\nNaN\n\n\nmean\n2018-10-16 00:00:00\nNaN\n11.470968\n\n\nmin\n2018-10-01 00:00:00\nNaN\n-1.100000\n\n\n25%\n2018-10-08 00:00:00\nNaN\n6.700000\n\n\n50%\n2018-10-16 00:00:00\nNaN\n11.700000\n\n\n75%\n2018-10-24 00:00:00\nNaN\n17.200000\n\n\nmax\n2018-10-31 00:00:00\nNaN\n26.700000\n\n\nstd\nNaN\nNaN\n7.362354\n\n\n\n\n\n\n\nPlotting long format data in pandas can be rather tricky. Instead we use seaborn:\n\nimport seaborn as sns\n\nsns.set(rc={'figure.figsize': (15, 5)}, style='white')\n\nax = sns.lineplot(\n data=long_df, x='date', y='value', hue='datatype'\n)\nax.set_ylabel('Temperature in Celsius')\nax.set_title('Temperature in NYC in October 2018')\nplt.show()\n\n\n\n\n\n\n\n\nWith long data and seaborn, we can easily facet our plots:\n\nsns.set(\n rc={'figure.figsize': (20, 10)}, style='white', font_scale=2\n)\n\ng = sns.FacetGrid(long_df, col='datatype', height=10)\ng = g.map(plt.plot, 'date', 'value')\ng.set_titles(size=25)\ng.set_xticklabels(rotation=45)\nplt.show()" + }, + { + "objectID": "notebooks/wide_vs_long.html#about-the-data", + "href": "notebooks/wide_vs_long.html#about-the-data", + "title": "Wide vs. Long Format Data", + "section": "", + "text": "In this notebook, we will be using daily temperature data from the National Centers for Environmental Information (NCEI) API. We will use the Global Historical Climatology Network - Daily (GHCND) dataset for the Boonton 1 station (GHCND:USC00280907); see the documentation here.\nNote: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one." + }, + { + "objectID": "notebooks/wide_vs_long.html#setup", + "href": "notebooks/wide_vs_long.html#setup", + "title": "Wide vs. Long Format Data", + "section": "", + "text": "import matplotlib.pyplot as plt\nimport pandas as pd\n\nwide_df = pd.read_csv('../data/wide_data.csv', parse_dates=['date'])\nlong_df = pd.read_csv(\n '../data/long_data.csv', \n usecols=['date', 'datatype', 'value'], \n parse_dates=['date']\n)[['date', 'datatype', 'value']] # sort columns" + }, + { + "objectID": "notebooks/wide_vs_long.html#wide-format", + "href": "notebooks/wide_vs_long.html#wide-format", + "title": "Wide vs. Long Format Data", + "section": "", + "text": "Our variables each have their own column:\n\nwide_df.head(6)\n\n\n\n\n\n\n\n\ndate\nTMAX\nTMIN\nTOBS\n\n\n\n\n0\n2018-10-01\n21.1\n8.9\n13.9\n\n\n1\n2018-10-02\n23.9\n13.9\n17.2\n\n\n2\n2018-10-03\n25.0\n15.6\n16.1\n\n\n3\n2018-10-04\n22.8\n11.7\n11.7\n\n\n4\n2018-10-05\n23.3\n11.7\n18.9\n\n\n5\n2018-10-06\n20.0\n13.3\n16.1\n\n\n\n\n\n\n\nDescribing all the columns is easy:\n\nwide_df.describe(include='all')\n\n\n\n\n\n\n\n\ndate\nTMAX\nTMIN\nTOBS\n\n\n\n\ncount\n31\n31.000000\n31.000000\n31.000000\n\n\nmean\n2018-10-16 00:00:00\n16.829032\n7.561290\n10.022581\n\n\nmin\n2018-10-01 00:00:00\n7.800000\n-1.100000\n-1.100000\n\n\n25%\n2018-10-08 12:00:00\n12.750000\n2.500000\n5.550000\n\n\n50%\n2018-10-16 00:00:00\n16.100000\n6.700000\n8.300000\n\n\n75%\n2018-10-23 12:00:00\n21.950000\n13.600000\n16.100000\n\n\nmax\n2018-10-31 00:00:00\n26.700000\n17.800000\n21.700000\n\n\nstd\nNaN\n5.714962\n6.513252\n6.596550\n\n\n\n\n\n\n\nIt’s easy to graph with pandas:\n\nwide_df.plot(\n x='date', y=['TMAX', 'TMIN', 'TOBS'], figsize=(15, 5), \n title='Temperature in NYC in October 2018'\n).set_ylabel('Temperature in Celsius')\nplt.show()" + }, + { + "objectID": "notebooks/wide_vs_long.html#long-format", + "href": "notebooks/wide_vs_long.html#long-format", + "title": "Wide vs. Long Format Data", + "section": "", + "text": "Our variable names are now in the datatype column and their values are in the value column. We now have 3 rows for each date, since we have 3 different datatypes:\n\nlong_df.head(6)\n\n\n\n\n\n\n\n\ndate\ndatatype\nvalue\n\n\n\n\n0\n2018-10-01\nTMAX\n21.1\n\n\n1\n2018-10-01\nTMIN\n8.9\n\n\n2\n2018-10-01\nTOBS\n13.9\n\n\n3\n2018-10-02\nTMAX\n23.9\n\n\n4\n2018-10-02\nTMIN\n13.9\n\n\n5\n2018-10-02\nTOBS\n17.2\n\n\n\n\n\n\n\nSince we have many rows for the same date, using describe() is not that helpful:\n\nlong_df.describe(include='all')\n\n\n\n\n\n\n\n\ndate\ndatatype\nvalue\n\n\n\n\ncount\n93\n93\n93.000000\n\n\nunique\nNaN\n3\nNaN\n\n\ntop\nNaN\nTMAX\nNaN\n\n\nfreq\nNaN\n31\nNaN\n\n\nmean\n2018-10-16 00:00:00\nNaN\n11.470968\n\n\nmin\n2018-10-01 00:00:00\nNaN\n-1.100000\n\n\n25%\n2018-10-08 00:00:00\nNaN\n6.700000\n\n\n50%\n2018-10-16 00:00:00\nNaN\n11.700000\n\n\n75%\n2018-10-24 00:00:00\nNaN\n17.200000\n\n\nmax\n2018-10-31 00:00:00\nNaN\n26.700000\n\n\nstd\nNaN\nNaN\n7.362354\n\n\n\n\n\n\n\nPlotting long format data in pandas can be rather tricky. Instead we use seaborn:\n\nimport seaborn as sns\n\nsns.set(rc={'figure.figsize': (15, 5)}, style='white')\n\nax = sns.lineplot(\n data=long_df, x='date', y='value', hue='datatype'\n)\nax.set_ylabel('Temperature in Celsius')\nax.set_title('Temperature in NYC in October 2018')\nplt.show()\n\n\n\n\n\n\n\n\nWith long data and seaborn, we can easily facet our plots:\n\nsns.set(\n rc={'figure.figsize': (20, 10)}, style='white', font_scale=2\n)\n\ng = sns.FacetGrid(long_df, col='datatype', height=10)\ng = g.map(plt.plot, 'date', 'value')\ng.set_titles(size=25)\ng.set_xticklabels(rotation=45)\nplt.show()" + }, + { + "objectID": "notebooks/python_errors.html", + "href": "notebooks/python_errors.html", + "title": "Python Errors", + "section": "", + "text": "When an error arises, there will be an error message with the type of error and the line the error occured on. This notebook goes over how to handle the common types of errors and exceptions in Python.\nI recommend looking at the Python tutorial page for more information on errors. Searching for the error message directly on Google can help the debugging process if there is an error not discussed in this page.\n\n\nA SyntaxError occurs when the syntax of your code is incorrect.\n\nif True \nprint(\"Hello World\")\n\nSyntaxError: expected ':' (975521850.py, line 1)\n\n\nA colon is expected after the if statement, which arises the syntax error. The error goes away after adding the colon.\n\nif True:\n print(\"Hello World\")\n\nHello World\n\n\n\n\n\nA NameError occurs when a variable, function, or module used does not exist. When this happens, it is usually because of a spelling error.\n\nadd\n\nNameError: name 'add' is not defined\n\n\n\nstring(9)\n\nNameError: name 'string' is not defined\n\n\n\n\n\nA TypeError occurs when you input an incorrect data type for an operation or function.\n\n\"abc\" + 9\n\nTypeError: can only concatenate str (not \"int\") to str\n\n\nIn python, you cannot add strings to integers. You can add, however, an integer to an integer or a string to a string with a +.\n\n9 + 9\n\n18\n\n\n\n\"abc\" + \"def\"\n\n'abcdef'\n\n\n\n\n\nA ZeroDivisionError occurs when you try to divide by zero. To fix this, recheck your computation.\n\n2 / (9 * 0)\n\nZeroDivisionError: division by zero\n\n\n\n#code corrected to no longer divide by zero\n(2 / 9) * 0\n\n0.0\n\n\n\n\n\nA ValueError occurs when an input for a function is the correct data type but is invalid in regards to the domain of the function. This is most common with mathematical operations.\n\nimport math\n\nmath.sqrt(-10)\n\nValueError: math domain error\n\n\nIn the example above, you must input a positive number into the sqrt() function. The negative number is still an integer, but it is not in the function’s domain.\n\nmath.sqrt(10)\n\n3.1622776601683795\n\n\n\n\n\nAn IndexError occurs when you try to access an item in a list with an index out of bounds.\n\nlist = [1,2,3,4,5]\nlist[5]\n\nIndexError: list index out of range\n\n\nThe range of a list is [0, n-1], where “n” is the length of the list. So, the list [1,2,3,4,5] has index elements in the range 0-4.\n\nlist[4]\n\n5\n\n\n\n\n\nA ModuleNotFoundError occurs when you try to import a module that does not exist. It is a type of ImportError. To fix this error, check if you have installed the module in your python environment from the terminal command-line.\n\nimport pillow\n\nModuleNotFoundError: No module named 'pillow'\n\n\n\n\n\nYou can use a try statement to catch errors. A try clause includes the code you want to run that might cause an error. If no error occurs, the try clause runs successfully. If an error does occur, the except clause runs after the line in the try clause that caused an error.\n\ntry:\n \"abc\" + 9\n print(\"Success\")\nexcept:\n print(\"Failure to execute\")\n\nFailure to execute\n\n\nThe except clause above can catch any type of error. However, an except clause can also catch a specific type of error. There can be mulptile except clauses in a try statement to catch the different types of errors.\n\ntry:\n hello\n \"abc\" + 9\n print(\"Success\")\nexcept TypeError:\n print(\"TypeError failure to execute\")\nexcept NameError:\n print(\"NameError failure to execute\")\n\nNameError failure to execute\n\n\n\ntry:\n list = [1,2,3,4,5]\n list[5]\n print(\"Success\")\nexcept TypeError:\n print(\"TypeError failure to execute\")\nexcept NameError:\n print(\"NameError failure to execute\")\nexcept IndexError:\n print(\"IndexError failure to execute\")\n\nIndexError failure to execute\n\n\n\n\n\nThese are not all the errors that might come up in your coding. If another type of error occurs, you can search the error type on Google to learn more about what has caused it. As always, remember to look at the line resulting in the error for hints on what could have gone wrong!" + }, + { + "objectID": "notebooks/python_errors.html#syntax-error", + "href": "notebooks/python_errors.html#syntax-error", + "title": "Python Errors", + "section": "", + "text": "A SyntaxError occurs when the syntax of your code is incorrect.\n\nif True \nprint(\"Hello World\")\n\nSyntaxError: expected ':' (975521850.py, line 1)\n\n\nA colon is expected after the if statement, which arises the syntax error. The error goes away after adding the colon.\n\nif True:\n print(\"Hello World\")\n\nHello World" + }, + { + "objectID": "notebooks/python_errors.html#name-error", + "href": "notebooks/python_errors.html#name-error", + "title": "Python Errors", + "section": "", + "text": "A NameError occurs when a variable, function, or module used does not exist. When this happens, it is usually because of a spelling error.\n\nadd\n\nNameError: name 'add' is not defined\n\n\n\nstring(9)\n\nNameError: name 'string' is not defined" + }, + { + "objectID": "notebooks/python_errors.html#type-error", + "href": "notebooks/python_errors.html#type-error", + "title": "Python Errors", + "section": "", + "text": "A TypeError occurs when you input an incorrect data type for an operation or function.\n\n\"abc\" + 9\n\nTypeError: can only concatenate str (not \"int\") to str\n\n\nIn python, you cannot add strings to integers. You can add, however, an integer to an integer or a string to a string with a +.\n\n9 + 9\n\n18\n\n\n\n\"abc\" + \"def\"\n\n'abcdef'" + }, + { + "objectID": "notebooks/python_errors.html#zero-division-error", + "href": "notebooks/python_errors.html#zero-division-error", + "title": "Python Errors", + "section": "", + "text": "A ZeroDivisionError occurs when you try to divide by zero. To fix this, recheck your computation.\n\n2 / (9 * 0)\n\nZeroDivisionError: division by zero\n\n\n\n#code corrected to no longer divide by zero\n(2 / 9) * 0\n\n0.0" + }, + { + "objectID": "notebooks/python_errors.html#value-error", + "href": "notebooks/python_errors.html#value-error", + "title": "Python Errors", + "section": "", + "text": "A ValueError occurs when an input for a function is the correct data type but is invalid in regards to the domain of the function. This is most common with mathematical operations.\n\nimport math\n\nmath.sqrt(-10)\n\nValueError: math domain error\n\n\nIn the example above, you must input a positive number into the sqrt() function. The negative number is still an integer, but it is not in the function’s domain.\n\nmath.sqrt(10)\n\n3.1622776601683795" + }, + { + "objectID": "notebooks/python_errors.html#index-error", + "href": "notebooks/python_errors.html#index-error", + "title": "Python Errors", + "section": "", + "text": "An IndexError occurs when you try to access an item in a list with an index out of bounds.\n\nlist = [1,2,3,4,5]\nlist[5]\n\nIndexError: list index out of range\n\n\nThe range of a list is [0, n-1], where “n” is the length of the list. So, the list [1,2,3,4,5] has index elements in the range 0-4.\n\nlist[4]\n\n5" + }, + { + "objectID": "notebooks/python_errors.html#module-not-found-error", + "href": "notebooks/python_errors.html#module-not-found-error", + "title": "Python Errors", + "section": "", + "text": "A ModuleNotFoundError occurs when you try to import a module that does not exist. It is a type of ImportError. To fix this error, check if you have installed the module in your python environment from the terminal command-line.\n\nimport pillow\n\nModuleNotFoundError: No module named 'pillow'" + }, + { + "objectID": "notebooks/python_errors.html#catching-exceptions-with-try-statements", + "href": "notebooks/python_errors.html#catching-exceptions-with-try-statements", + "title": "Python Errors", + "section": "", + "text": "You can use a try statement to catch errors. A try clause includes the code you want to run that might cause an error. If no error occurs, the try clause runs successfully. If an error does occur, the except clause runs after the line in the try clause that caused an error.\n\ntry:\n \"abc\" + 9\n print(\"Success\")\nexcept:\n print(\"Failure to execute\")\n\nFailure to execute\n\n\nThe except clause above can catch any type of error. However, an except clause can also catch a specific type of error. There can be mulptile except clauses in a try statement to catch the different types of errors.\n\ntry:\n hello\n \"abc\" + 9\n print(\"Success\")\nexcept TypeError:\n print(\"TypeError failure to execute\")\nexcept NameError:\n print(\"NameError failure to execute\")\n\nNameError failure to execute\n\n\n\ntry:\n list = [1,2,3,4,5]\n list[5]\n print(\"Success\")\nexcept TypeError:\n print(\"TypeError failure to execute\")\nexcept NameError:\n print(\"NameError failure to execute\")\nexcept IndexError:\n print(\"IndexError failure to execute\")\n\nIndexError failure to execute" + }, + { + "objectID": "notebooks/python_errors.html#next-steps", + "href": "notebooks/python_errors.html#next-steps", + "title": "Python Errors", + "section": "", + "text": "These are not all the errors that might come up in your coding. If another type of error occurs, you can search the error type on Google to learn more about what has caused it. As always, remember to look at the line resulting in the error for hints on what could have gone wrong!" + }, + { + "objectID": "notebooks/plotting_with_pandas.html", + "href": "notebooks/plotting_with_pandas.html", + "title": "Plotting with Pandas", + "section": "", + "text": "The plot() method is available on Series and DataFrame objects. Many of the parameters get passed down to matplotlib. The kind argument let’s us vary the plot type. Here are some commonly used parameters:\n\n\n\n\n\n\n\n\nParameter\nPurpose\nData Type\n\n\n\n\nkind\nDetermines the plot type\nString\n\n\nx/y\nColumn(s) to plot on the x-axis/y-axis\nString or list\n\n\nax\nDraws the plot on the Axes object provided\nAxes\n\n\nsubplots\nDetermines whether to make subplots\nBoolean\n\n\nlayout\nSpecifies how to arrange the subplots\nTuple of (rows, columns)\n\n\nfigsize\nSize to make the Figure object\nTuple of (width, height)\n\n\ntitle\nThe title of the plot or subplots\nString for the plot title or a list of strings for subplot titles\n\n\nlegend\nDetermines whether to show the legend\nBoolean\n\n\nlabel\nWhat to call an item in the legend\nString if a single column is being plotted; otherwise, a list of strings\n\n\nstyle\nmatplotlib style strings for each item being plotted\nString if a single column is being plotted; otherwise, a list of strings\n\n\ncolor\nThe color to plot the item in\nString or red, green, blue tuple if a single column is being plotted; otherwise, a list\n\n\ncolormap\nThe colormap to use\nString or matplotlib colormap object\n\n\nlogx/logy/loglog\nDetermines whether to use a logarithmic scale for the x-axis, y-axis, or both\nBoolean\n\n\nxticks/yticks\nDetermines where to draw the ticks on the x-axis/y-axis\nList of values\n\n\nxlim/ylim\nThe axis limits for the x-axis/y-axis\nTuple of the form (min, max)\n\n\nrot\nThe angle to write the tick labels at\nInteger\n\n\nsharex/sharey\nDetermines whether to have subplots share the x-axis/y-axis\nBoolean\n\n\nfontsize\nControls the size of the tick labels\nInteger\n\n\ngrid\nTurns on/off the grid lines\nBoolean\n\n\n\n\n\nIn this notebook, we will be working with 3 datasets: - Facebook’s stock price throughout 2018 (obtained using the stock_analysis package) - Earthquake data from September 18, 2018 - October 13, 2018 (obtained from the US Geological Survey (USGS) using the USGS API) - European Centre for Disease Prevention and Control’s (ECDC) daily number of new reported cases of COVID-19 by country worldwide dataset collected on September 19, 2020 via this link\n\n\n\n\n%matplotlib inline\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)\nquakes = pd.read_csv('../data/earthquakes.csv')\ncovid = pd.read_csv('../data/covid19_cases.csv').assign(\n date=lambda x: pd.to_datetime(x.dateRep, format='%d/%m/%Y')\n).set_index('date').replace(\n 'United_States_of_America', 'USA'\n).sort_index()['2020-01-18':'2020-09-18']\n\n\n\n\nLine plots help us see how a variable changes over time. They are the default for the kind argument, but we can pass kind='line' to be explicit in our intent:\n\nfb.plot(\n kind='line',\n y='open',\n figsize=(10, 5),\n style='-b',\n legend=False,\n title='Evolution of Facebook Open Price'\n)\n\n\n\n\n\n\n\n\nWe provided the style argument in the previous example; however, we can use the color and linestyle arguments to get the same result:\n\nfb.plot(\n kind='line',\n y='open',\n figsize=(10, 5),\n color='blue',\n linestyle='solid',\n legend=False,\n title='Evolution of Facebook Open Price'\n)\n\n\n\n\n\n\n\n\nWe can also plot many lines at once by simply passing a list of the columns to plot:\n\nfb.first('1W').plot(\n y=['open', 'high', 'low', 'close'],\n style=['o-b', '--r', ':k', '.-g'],\n title='Facebook OHLC Prices during 1st Week of Trading 2018'\n).autoscale()\n\n\n\n\n\n\n\n\n\n\nWhen plotting with pandas, creating subplots is simply a matter of passing subplots=True to the plot() method, and (optionally) specifying the layout in a tuple of (rows, columns):\n\nfb.plot(\n kind='line',\n subplots=True,\n layout=(3, 2),\n figsize=(15, 10),\n title='Facebook Stock 2018'\n)\n\narray([[<Axes: xlabel='date'>, <Axes: xlabel='date'>],\n [<Axes: xlabel='date'>, <Axes: xlabel='date'>],\n [<Axes: xlabel='date'>, <Axes: xlabel='date'>]], dtype=object)\n\n\n\n\n\n\n\n\n\nNote that we didn’t provide a specific column to plot and pandas plotted all of them for us.\nSometimes we want to make subplots that each have a few variables in them for comparison. This can be achieved using the ax parameter. To illustrate this, let’s take a look at daily new COVID-19 cases in China, Spain, Italy, the USA, Brazil, and India:\n\nnew_cases_rolling_average = covid.pivot_table(\n index=covid.index, \n columns='countriesAndTerritories', \n values='cases'\n).rolling(7).mean()\n\nSince there is a lot of fluctuation in these values, we will plot the 7-day moving average of new cases using the rolling() method (discussed in chapter 4). Rather than create a separate plot for each country (which makes it harder to compare) or plot them all together (which will make it difficult to see the smaller values), we will plot countries that have had a similar number of cases in the same subplot:\n\nfig, axes = plt.subplots(1, 3, figsize=(15, 5))\n\nnew_cases_rolling_average[['China']].plot(ax=axes[0], style='-.c')\nnew_cases_rolling_average[['Italy', 'Spain']].plot(\n ax=axes[1], style=['-', '--'], \n title='7-day rolling average of new COVID-19 cases\\n(source: ECDC)'\n)\nnew_cases_rolling_average[['Brazil', 'India', 'USA']]\\\n .plot(ax=axes[2], style=['--', ':', '-'])\n\n\n\n\n\n\n\n\nNOTE: we specified the line styles here so that the lines can be distinguished in the text as a black and white image.\nIn the previous figure, we were able to compare countries with similar levels of new COVID-19 cases, but we couldn’t compare all of them in the same plot due to scale. One way around this is to use an area plot, which makes it possible for us to visualize the overall 7-day rolling average of new COVID-19 cases and at the same time how much each country is contributing to the total. In the interest of readability, we will group Italy and Spain together and create another category for countries other than the USA, Brazil, and India. The combined height of the plot areas is the overall value, and the height of given shaded region is the value for the individual country.\n\nplot_cols = ['Brazil', 'India', 'Italy & Spain', 'USA', 'Other']\ngrouped = ['Italy', 'Spain']\nother_cols = [\n col for col in new_cases_rolling_average.columns \n if col not in plot_cols\n]\n\nnew_cases_rolling_average.sort_index(axis=1).assign(\n **{\n 'Italy & Spain': lambda x: x[grouped].sum(axis=1),\n 'Other': lambda x: x[other_cols].drop(columns=grouped).sum(axis=1)\n }\n)[plot_cols].plot(\n kind='area', figsize=(15, 5), \n title='7-day rolling average of new COVID-19 cases\\n(source: ECDC)'\n)\n\n\n\n\n\n\n\n\nAnother way to visualize evolution over time is to look at the cumulative sum over time. Let’s plot the cumulative number of COVID-19 cases in China, Spain, Italy, the USA, Brazil, and India, using ax to create subplots as we did in the previous example.\n\nfig, axes = plt.subplots(1, 3, figsize=(15, 3))\n\ncumulative_covid_cases = covid.groupby(\n ['countriesAndTerritories', pd.Grouper(freq='1D')]\n).cases.sum().unstack(0).apply('cumsum')\n\ncumulative_covid_cases[['China']].plot(ax=axes[0], style='-.c')\ncumulative_covid_cases[['Italy', 'Spain']].plot(\n ax=axes[1], style=['-', '--'], \n title='Cumulative COVID-19 Cases\\n(source: ECDC)'\n)\ncumulative_covid_cases[['Brazil', 'India', 'USA']]\\\n .plot(ax=axes[2], style=['--', ':', '-'])\n\n\n\n\n\n\n\n\nNOTE: we specified the line styles here so that the lines can be distinguished in the text as a black and white image.\n\n\n\n\n\n\nWe make scatter plots to help visualize the relationship between two variables. Creating scatter plots requires we pass in kind='scatter' along with a column for the x-axis and a column for the y-axis:\n\nfb.assign(\n max_abs_change=fb.high - fb.low\n).plot(\n kind='scatter', x='volume', y='max_abs_change',\n title='Facebook Daily High - Low vs. Volume Traded'\n)\n\n\n\n\n\n\n\n\nThe relationship doesn’t seem to be linear, but we can try a log transform on the x-axis since the scales of the axes are very different. With pandas, we simply pass in logx=True:\n\nfb.assign(\n max_abs_change=fb.high - fb.low\n).plot(\n kind='scatter', x='volume', y='max_abs_change',\n title='Facebook Daily High - Low vs. log(Volume Traded)', \n logx=True\n)\n\n\n\n\n\n\n\n\nWith matplotlib, we could use plt.xscale('log') to do the same thing.\n\n\n\nSometimes our plots have many overlapping values, but this can be impossible to see. This can be addressed by increasing the transparency of what we are plotting using the alpha parameter. It is a float in the range [0, 1] where 0 is completely transparent and 1 is completely opaque. By default this is 1, so let’s put in a lower value and re-plot the scatter plot:\n\nfb.assign(\n max_abs_change=fb.high - fb.low\n).plot(\n kind='scatter', x='volume', y='max_abs_change',\n title='Facebook Daily High - Low vs. log(Volume Traded)',\n logx=True, alpha=0.25\n)\n\n\n\n\n\n\n\n\n\n\n\nIn the previous example, we can start to see the overlaps, but it is still difficult. Hexbins are another plot type that divide up the plot into hexagons, which are shaded according to the density of points there. With pandas, this is the hexbin value for the kind argument. It may also be necessary to tweak the gridsize, which determines the number of hexagons along the y-axis:\n\nfb.assign(\n log_volume=np.log(fb.volume),\n max_abs_change=fb.high - fb.low\n).plot(\n kind='hexbin',\n x='log_volume',\n y='max_abs_change',\n title='Facebook Daily High - Low vs. log(Volume Traded)',\n colormap='gray_r',\n gridsize=20, \n sharex=False # we have to pass this to see the x-axis\n)\n\n\n\n\n\n\n\n\n\n\n\nPandas doesn’t offer heatmaps; however, if we are able to get our data into a matrix, we can use matshow() from matplotlib:\n\nfig, ax = plt.subplots(figsize=(20, 10))\n\n# calculate the correlation matrix\nfb_corr = fb.assign(\n log_volume=np.log(fb.volume),\n max_abs_change=fb.high - fb.low\n).corr()\n\n# create the heatmap and colorbar\nim = ax.matshow(fb_corr, cmap='seismic')\nim.set_clim(-1, 1)\nfig.colorbar(im)\n\n# label the ticks with the column names\nlabels = [col.lower() for col in fb_corr.columns]\nax.set_xticks(ax.get_xticks()[1:-1]) # to handle bug in matplotlib\nax.set_xticklabels(labels, rotation=45)\nax.set_yticks(ax.get_yticks()[1:-1]) # to handle bug in matplotlib\nax.set_yticklabels(labels)\n\n# include the value of the correlation coefficient in the boxes\nfor (i, j), coef in np.ndenumerate(fb_corr):\n ax.text(\n i, j, fr'$\\rho$ = {coef:.2f}', # raw (r), format (f) string\n ha='center', va='center', \n color='white', fontsize=14\n )\n\n\n\n\n\n\n\n\nAccessing the values in the correlation matrix can be done with loc[]:\n\nfb_corr.loc['max_abs_change', ['volume', 'log_volume']]\n\nvolume 0.642027\nlog_volume 0.731542\nName: max_abs_change, dtype: float64\n\n\n\n\n\n\n\n\nWith the pandas, making histograms is as easy as passing kind='hist' to the plot() method:\n\nfb.volume.plot(\n kind='hist', \n title='Histogram of Daily Volume Traded in Facebook Stock'\n)\nplt.xlabel('Volume traded') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'Volume traded')\n\n\n\n\n\n\n\n\n\nWe can overlap histograms to compare distributions provided we use the alpha parameter. For example, let’s compare the usage and magnitude of the various measurement techniques (the magType column) in the data:\n\nfig, axes = plt.subplots(figsize=(8, 5))\n\nfor magtype in quakes.magType.unique():\n data = quakes.query(f'magType == \"{magtype}\"').mag\n if not data.empty:\n data.plot(\n kind='hist', ax=axes, alpha=0.4, \n label=magtype, legend=True,\n title='Comparing histograms of earthquake magnitude by magType'\n )\n\nplt.xlabel('magnitude') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'magnitude')\n\n\n\n\n\n\n\n\n\n\n\n\nWe can pass kind='kde' for an estimate of the probability density function (PDF), which tells us the probability of getting a particular value:\n\nfb.high.plot(\n kind='kde', \n title='KDE of Daily High Price for Facebook Stock'\n)\nplt.xlabel('Price ($)') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'Price ($)')\n\n\n\n\n\n\n\n\n\n\n\n\nThe plot() method returns an Axes object. We can store this for additional customization of the plot, or we can pass this into another call to plot() as the ax argument to add to the original plot.\nIt can often be helpful to view the KDE superimposed on top of the histogram, which can be achieved with this strategy:\n\nax = fb.high.plot(kind='hist', density=True, alpha=0.5)\nfb.high.plot(\n ax=ax, kind='kde', color='blue', \n title='Distribution of Facebook Stock\\'s Daily High Price in 2018'\n)\nplt.xlabel('Price ($)') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'Price ($)')\n\n\n\n\n\n\n\n\n\n\n\n\nIn some cases, we are more interested in the probability of getting less than or equal to that value (or greater than or equal), which we can see with the cumulative disribution function (CDF). Using the statsmodels package, we can estimate the CDF giving us the empirical cumulative distribution function (ECDF):\n\nfrom statsmodels.distributions.empirical_distribution import ECDF\n\necdf = ECDF(quakes.query('magType == \"ml\"').mag)\nplt.plot(ecdf.x, ecdf.y)\n\n# axis labels (we will cover this in chapter 6)\nplt.xlabel('mag') # add x-axis label \nplt.ylabel('cumulative probability') # add y-axis label\n\n# add title (we will cover this in chapter 6)\nplt.title('ECDF of earthquake magnitude with magType ml')\n\nText(0.5, 1.0, 'ECDF of earthquake magnitude with magType ml')\n\n\n\n\n\n\n\n\n\nThis ECDF tells us the probability of getting an earthquake with magnitude of 3 or less using the ml scale is 98%:\n\nfrom statsmodels.distributions.empirical_distribution import ECDF\n\necdf = ECDF(quakes.query('magType == \"ml\"').mag)\nplt.plot(ecdf.x, ecdf.y)\n\n# formatting below will all be covered in chapter 6\n# axis labels\nplt.xlabel('mag') # add x-axis label \nplt.ylabel('cumulative probability') # add y-axis label\n\n# add reference lines for interpreting the ECDF for mag <= 3 \nplt.plot(\n [3, 3], [0, .98], '--k', \n [-1.5, 3], [0.98, 0.98], '--k', alpha=0.4\n)\n\n# set axis ranges\nplt.ylim(0, None)\nplt.xlim(-1.25, None)\n\n# add a title\nplt.title('P(mag <= 3) = 98%')\n\nText(0.5, 1.0, 'P(mag <= 3) = 98%')\n\n\n\n\n\n\n\n\n\n\n\n\nTo make box plots with pandas, we pass kind='box' to the plot() method:\n\nfb.iloc[:,:4].plot(kind='box', title='Facebook OHLC Prices Box Plot')\nplt.ylabel('price ($)') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'price ($)')\n\n\n\n\n\n\n\n\n\nIf we pass in notch=True, we get a notched box plot. The notch represents a 95% confidence interval around the median, which can be helpful when comparing differences. For an introduction to interpreting a notched box plot, see this Google sites page and this Towards Data Science article.\n\nfb.iloc[:,:4].plot(kind='box', title='Facebook OHLC Prices Box Plot', notch=True)\nplt.ylabel('price ($)') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'price ($)')\n\n\n\n\n\n\n\n\n\nThis can also be combined with a call to groupby():\n\nfb.assign(\n volume_bin=pd.cut(fb.volume, 3, labels=['low', 'med', 'high'])\n).groupby('volume_bin').boxplot(\n column=['open', 'high', 'low', 'close'],\n layout=(1, 3), figsize=(12, 3)\n)\nplt.suptitle('Facebook OHLC Box Plots by Volume Traded', y=1.1)\n\nText(0.5, 1.1, 'Facebook OHLC Box Plots by Volume Traded')\n\n\n\n\n\n\n\n\n\nWe can use this to see the distribution of magnitudes across the different measurement methods for earthquakes:\n\nquakes[['mag', 'magType']].groupby('magType').boxplot(\n figsize=(15, 8), subplots=False\n)\nplt.title('Earthquake Magnitude Box Plots by magType')\nplt.ylabel('magnitude') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'magnitude')\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nPassing kind='barh' gives us horizontal bars while kind='bar' gives us vertical ones. Let’s use horizontal bars to look at the top 15 places for earthquakes in our data:\n\nquakes.parsed_place.value_counts().iloc[14::-1,].plot(\n kind='barh', figsize=(10, 5),\n title='Top 15 Places for Earthquakes '\n '(September 18, 2018 - October 13, 2018)'\n)\nplt.xlabel('earthquakes') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'earthquakes')\n\n\n\n\n\n\n\n\n\nWe also have data on whether earthquakes were accompanied by tsunamis. Let’s see what the top places for tsunamis are:\n\nquakes.groupby('parsed_place').tsunami.sum().sort_values().iloc[-10:,].plot(\n kind='barh', figsize=(10, 5), \n title='Top 10 Places for Tsunamis '\n '(September 18, 2018 - October 13, 2018)'\n)\nplt.xlabel('tsunamis') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'tsunamis')\n\n\n\n\n\n\n\n\n\nSeeing that Indonesia is the top place for tsunamis during the time period we are looking at, we may want to look how many earthquakes and tsunamis Indonesia gets on a daily basis. We could show this as a line plot or with bars; since we don’t want to interpolate, we will use bars here:\n\nindonesia_quakes = quakes.query('parsed_place == \"Indonesia\"').assign(\n time=lambda x: pd.to_datetime(x.time, unit='ms'),\n earthquake=1\n).set_index('time').resample('1D').sum()\n\n# format the datetimes in the index for the x-axis\nindonesia_quakes.index = indonesia_quakes.index.strftime('%b\\n%d')\n\nindonesia_quakes.plot(\n y=['earthquake', 'tsunami'], kind='bar', figsize=(15, 3), \n rot=0, label=['earthquakes', 'tsunamis'], \n title='Earthquakes and Tsunamis in Indonesia '\n '(September 18, 2018 - October 13, 2018)'\n)\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('date')\nplt.ylabel('count')\n\nC:\\Users\\gpower\\AppData\\Local\\Temp\\ipykernel_13112\\3940988219.py:4: FutureWarning: The default value of numeric_only in DataFrameGroupBy.sum is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.\n ).set_index('time').resample('1D').sum()\n\n\nText(0, 0.5, 'count')\n\n\n\n\n\n\n\n\n\n\n\n\n\nquakes.groupby(['parsed_place', 'tsunami']).mag.count()\\\n .unstack().apply(lambda x: x / x.sum(), axis=1)\\\n .rename(columns={0: 'no', 1: 'yes'})\\\n .sort_values('yes', ascending=False)[7::-1]\\\n .plot.barh(\n title='Frequency of a tsunami accompanying an earthquake'\n )\n\n# move legend to the right of the plot\nplt.legend(title='tsunami?', bbox_to_anchor=(1, 0.65))\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('percentage of earthquakes')\nplt.ylabel('')\n\nText(0, 0.5, '')\n\n\n\n\n\n\n\n\n\nUsing the kind arugment for vertical bars when the labels for each bar are shorter:\n\nquakes.magType.value_counts().plot(\n kind='bar', title='Earthquakes Recorded per magType', rot=0\n)\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('magType')\nplt.ylabel('earthquakes')\n\nText(0, 0.5, 'earthquakes')\n\n\n\n\n\n\n\n\n\n\n\n\n\npivot = quakes.assign(\n mag_bin=lambda x: np.floor(x.mag)\n).pivot_table(\n index='mag_bin', columns='magType', values='mag', aggfunc='count'\n)\npivot.plot.bar(\n stacked=True, rot=0, ylabel='earthquakes', \n title='Earthquakes by integer magnitude and magType'\n)\n\n\n\n\n\n\n\n\n\n\nPlot the percentages to be better able to see the different magTypes.\n\nnormalized_pivot = pivot.fillna(0).apply(lambda x: x / x.sum(), axis=1)\nax = normalized_pivot.plot.bar(\n stacked=True, rot=0, figsize=(10, 5),\n title='Percentage of earthquakes by integer magnitude for each magType'\n)\nax.legend(bbox_to_anchor=(1, 0.8)) # move legend to the right of the plot\nplt.ylabel('percentage') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'percentage')\n\n\n\n\n\n\n\n\n\nWe can also create horizontal stacked bars and do so using groupby() and unstack():\n\nquakes.groupby(['parsed_place', 'tsunami']).mag.count()\\\n .unstack().apply(lambda x: x / x.sum(), axis=1)\\\n .rename(columns={0: 'no', 1: 'yes'})\\\n .sort_values('yes', ascending=False)[7::-1]\\\n .plot.barh(\n title='Frequency of a tsunami accompanying an earthquake', \n stacked=True\n )\n\n# move legend to the right of the plot\nplt.legend(title='tsunami?', bbox_to_anchor=(1, 0.65))\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('percentage of earthquakes')\nplt.ylabel('')\n\nText(0, 0.5, '')" + }, + { + "objectID": "notebooks/plotting_with_pandas.html#about-the-data", + "href": "notebooks/plotting_with_pandas.html#about-the-data", + "title": "Plotting with Pandas", + "section": "", + "text": "In this notebook, we will be working with 3 datasets: - Facebook’s stock price throughout 2018 (obtained using the stock_analysis package) - Earthquake data from September 18, 2018 - October 13, 2018 (obtained from the US Geological Survey (USGS) using the USGS API) - European Centre for Disease Prevention and Control’s (ECDC) daily number of new reported cases of COVID-19 by country worldwide dataset collected on September 19, 2020 via this link" + }, + { + "objectID": "notebooks/plotting_with_pandas.html#setup", + "href": "notebooks/plotting_with_pandas.html#setup", + "title": "Plotting with Pandas", + "section": "", + "text": "%matplotlib inline\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)\nquakes = pd.read_csv('../data/earthquakes.csv')\ncovid = pd.read_csv('../data/covid19_cases.csv').assign(\n date=lambda x: pd.to_datetime(x.dateRep, format='%d/%m/%Y')\n).set_index('date').replace(\n 'United_States_of_America', 'USA'\n).sort_index()['2020-01-18':'2020-09-18']" + }, + { + "objectID": "notebooks/plotting_with_pandas.html#evolution-over-time", + "href": "notebooks/plotting_with_pandas.html#evolution-over-time", + "title": "Plotting with Pandas", + "section": "", + "text": "Line plots help us see how a variable changes over time. They are the default for the kind argument, but we can pass kind='line' to be explicit in our intent:\n\nfb.plot(\n kind='line',\n y='open',\n figsize=(10, 5),\n style='-b',\n legend=False,\n title='Evolution of Facebook Open Price'\n)\n\n\n\n\n\n\n\n\nWe provided the style argument in the previous example; however, we can use the color and linestyle arguments to get the same result:\n\nfb.plot(\n kind='line',\n y='open',\n figsize=(10, 5),\n color='blue',\n linestyle='solid',\n legend=False,\n title='Evolution of Facebook Open Price'\n)\n\n\n\n\n\n\n\n\nWe can also plot many lines at once by simply passing a list of the columns to plot:\n\nfb.first('1W').plot(\n y=['open', 'high', 'low', 'close'],\n style=['o-b', '--r', ':k', '.-g'],\n title='Facebook OHLC Prices during 1st Week of Trading 2018'\n).autoscale()\n\n\n\n\n\n\n\n\n\n\nWhen plotting with pandas, creating subplots is simply a matter of passing subplots=True to the plot() method, and (optionally) specifying the layout in a tuple of (rows, columns):\n\nfb.plot(\n kind='line',\n subplots=True,\n layout=(3, 2),\n figsize=(15, 10),\n title='Facebook Stock 2018'\n)\n\narray([[<Axes: xlabel='date'>, <Axes: xlabel='date'>],\n [<Axes: xlabel='date'>, <Axes: xlabel='date'>],\n [<Axes: xlabel='date'>, <Axes: xlabel='date'>]], dtype=object)\n\n\n\n\n\n\n\n\n\nNote that we didn’t provide a specific column to plot and pandas plotted all of them for us.\nSometimes we want to make subplots that each have a few variables in them for comparison. This can be achieved using the ax parameter. To illustrate this, let’s take a look at daily new COVID-19 cases in China, Spain, Italy, the USA, Brazil, and India:\n\nnew_cases_rolling_average = covid.pivot_table(\n index=covid.index, \n columns='countriesAndTerritories', \n values='cases'\n).rolling(7).mean()\n\nSince there is a lot of fluctuation in these values, we will plot the 7-day moving average of new cases using the rolling() method (discussed in chapter 4). Rather than create a separate plot for each country (which makes it harder to compare) or plot them all together (which will make it difficult to see the smaller values), we will plot countries that have had a similar number of cases in the same subplot:\n\nfig, axes = plt.subplots(1, 3, figsize=(15, 5))\n\nnew_cases_rolling_average[['China']].plot(ax=axes[0], style='-.c')\nnew_cases_rolling_average[['Italy', 'Spain']].plot(\n ax=axes[1], style=['-', '--'], \n title='7-day rolling average of new COVID-19 cases\\n(source: ECDC)'\n)\nnew_cases_rolling_average[['Brazil', 'India', 'USA']]\\\n .plot(ax=axes[2], style=['--', ':', '-'])\n\n\n\n\n\n\n\n\nNOTE: we specified the line styles here so that the lines can be distinguished in the text as a black and white image.\nIn the previous figure, we were able to compare countries with similar levels of new COVID-19 cases, but we couldn’t compare all of them in the same plot due to scale. One way around this is to use an area plot, which makes it possible for us to visualize the overall 7-day rolling average of new COVID-19 cases and at the same time how much each country is contributing to the total. In the interest of readability, we will group Italy and Spain together and create another category for countries other than the USA, Brazil, and India. The combined height of the plot areas is the overall value, and the height of given shaded region is the value for the individual country.\n\nplot_cols = ['Brazil', 'India', 'Italy & Spain', 'USA', 'Other']\ngrouped = ['Italy', 'Spain']\nother_cols = [\n col for col in new_cases_rolling_average.columns \n if col not in plot_cols\n]\n\nnew_cases_rolling_average.sort_index(axis=1).assign(\n **{\n 'Italy & Spain': lambda x: x[grouped].sum(axis=1),\n 'Other': lambda x: x[other_cols].drop(columns=grouped).sum(axis=1)\n }\n)[plot_cols].plot(\n kind='area', figsize=(15, 5), \n title='7-day rolling average of new COVID-19 cases\\n(source: ECDC)'\n)\n\n\n\n\n\n\n\n\nAnother way to visualize evolution over time is to look at the cumulative sum over time. Let’s plot the cumulative number of COVID-19 cases in China, Spain, Italy, the USA, Brazil, and India, using ax to create subplots as we did in the previous example.\n\nfig, axes = plt.subplots(1, 3, figsize=(15, 3))\n\ncumulative_covid_cases = covid.groupby(\n ['countriesAndTerritories', pd.Grouper(freq='1D')]\n).cases.sum().unstack(0).apply('cumsum')\n\ncumulative_covid_cases[['China']].plot(ax=axes[0], style='-.c')\ncumulative_covid_cases[['Italy', 'Spain']].plot(\n ax=axes[1], style=['-', '--'], \n title='Cumulative COVID-19 Cases\\n(source: ECDC)'\n)\ncumulative_covid_cases[['Brazil', 'India', 'USA']]\\\n .plot(ax=axes[2], style=['--', ':', '-'])\n\n\n\n\n\n\n\n\nNOTE: we specified the line styles here so that the lines can be distinguished in the text as a black and white image." + }, + { + "objectID": "notebooks/plotting_with_pandas.html#visualizing-relationships-between-variables", + "href": "notebooks/plotting_with_pandas.html#visualizing-relationships-between-variables", + "title": "Plotting with Pandas", + "section": "", + "text": "We make scatter plots to help visualize the relationship between two variables. Creating scatter plots requires we pass in kind='scatter' along with a column for the x-axis and a column for the y-axis:\n\nfb.assign(\n max_abs_change=fb.high - fb.low\n).plot(\n kind='scatter', x='volume', y='max_abs_change',\n title='Facebook Daily High - Low vs. Volume Traded'\n)\n\n\n\n\n\n\n\n\nThe relationship doesn’t seem to be linear, but we can try a log transform on the x-axis since the scales of the axes are very different. With pandas, we simply pass in logx=True:\n\nfb.assign(\n max_abs_change=fb.high - fb.low\n).plot(\n kind='scatter', x='volume', y='max_abs_change',\n title='Facebook Daily High - Low vs. log(Volume Traded)', \n logx=True\n)\n\n\n\n\n\n\n\n\nWith matplotlib, we could use plt.xscale('log') to do the same thing.\n\n\n\nSometimes our plots have many overlapping values, but this can be impossible to see. This can be addressed by increasing the transparency of what we are plotting using the alpha parameter. It is a float in the range [0, 1] where 0 is completely transparent and 1 is completely opaque. By default this is 1, so let’s put in a lower value and re-plot the scatter plot:\n\nfb.assign(\n max_abs_change=fb.high - fb.low\n).plot(\n kind='scatter', x='volume', y='max_abs_change',\n title='Facebook Daily High - Low vs. log(Volume Traded)',\n logx=True, alpha=0.25\n)\n\n\n\n\n\n\n\n\n\n\n\nIn the previous example, we can start to see the overlaps, but it is still difficult. Hexbins are another plot type that divide up the plot into hexagons, which are shaded according to the density of points there. With pandas, this is the hexbin value for the kind argument. It may also be necessary to tweak the gridsize, which determines the number of hexagons along the y-axis:\n\nfb.assign(\n log_volume=np.log(fb.volume),\n max_abs_change=fb.high - fb.low\n).plot(\n kind='hexbin',\n x='log_volume',\n y='max_abs_change',\n title='Facebook Daily High - Low vs. log(Volume Traded)',\n colormap='gray_r',\n gridsize=20, \n sharex=False # we have to pass this to see the x-axis\n)\n\n\n\n\n\n\n\n\n\n\n\nPandas doesn’t offer heatmaps; however, if we are able to get our data into a matrix, we can use matshow() from matplotlib:\n\nfig, ax = plt.subplots(figsize=(20, 10))\n\n# calculate the correlation matrix\nfb_corr = fb.assign(\n log_volume=np.log(fb.volume),\n max_abs_change=fb.high - fb.low\n).corr()\n\n# create the heatmap and colorbar\nim = ax.matshow(fb_corr, cmap='seismic')\nim.set_clim(-1, 1)\nfig.colorbar(im)\n\n# label the ticks with the column names\nlabels = [col.lower() for col in fb_corr.columns]\nax.set_xticks(ax.get_xticks()[1:-1]) # to handle bug in matplotlib\nax.set_xticklabels(labels, rotation=45)\nax.set_yticks(ax.get_yticks()[1:-1]) # to handle bug in matplotlib\nax.set_yticklabels(labels)\n\n# include the value of the correlation coefficient in the boxes\nfor (i, j), coef in np.ndenumerate(fb_corr):\n ax.text(\n i, j, fr'$\\rho$ = {coef:.2f}', # raw (r), format (f) string\n ha='center', va='center', \n color='white', fontsize=14\n )\n\n\n\n\n\n\n\n\nAccessing the values in the correlation matrix can be done with loc[]:\n\nfb_corr.loc['max_abs_change', ['volume', 'log_volume']]\n\nvolume 0.642027\nlog_volume 0.731542\nName: max_abs_change, dtype: float64" + }, + { + "objectID": "notebooks/plotting_with_pandas.html#visualizing-distributions", + "href": "notebooks/plotting_with_pandas.html#visualizing-distributions", + "title": "Plotting with Pandas", + "section": "", + "text": "With the pandas, making histograms is as easy as passing kind='hist' to the plot() method:\n\nfb.volume.plot(\n kind='hist', \n title='Histogram of Daily Volume Traded in Facebook Stock'\n)\nplt.xlabel('Volume traded') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'Volume traded')\n\n\n\n\n\n\n\n\n\nWe can overlap histograms to compare distributions provided we use the alpha parameter. For example, let’s compare the usage and magnitude of the various measurement techniques (the magType column) in the data:\n\nfig, axes = plt.subplots(figsize=(8, 5))\n\nfor magtype in quakes.magType.unique():\n data = quakes.query(f'magType == \"{magtype}\"').mag\n if not data.empty:\n data.plot(\n kind='hist', ax=axes, alpha=0.4, \n label=magtype, legend=True,\n title='Comparing histograms of earthquake magnitude by magType'\n )\n\nplt.xlabel('magnitude') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'magnitude')\n\n\n\n\n\n\n\n\n\n\n\n\nWe can pass kind='kde' for an estimate of the probability density function (PDF), which tells us the probability of getting a particular value:\n\nfb.high.plot(\n kind='kde', \n title='KDE of Daily High Price for Facebook Stock'\n)\nplt.xlabel('Price ($)') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'Price ($)')\n\n\n\n\n\n\n\n\n\n\n\n\nThe plot() method returns an Axes object. We can store this for additional customization of the plot, or we can pass this into another call to plot() as the ax argument to add to the original plot.\nIt can often be helpful to view the KDE superimposed on top of the histogram, which can be achieved with this strategy:\n\nax = fb.high.plot(kind='hist', density=True, alpha=0.5)\nfb.high.plot(\n ax=ax, kind='kde', color='blue', \n title='Distribution of Facebook Stock\\'s Daily High Price in 2018'\n)\nplt.xlabel('Price ($)') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'Price ($)')\n\n\n\n\n\n\n\n\n\n\n\n\nIn some cases, we are more interested in the probability of getting less than or equal to that value (or greater than or equal), which we can see with the cumulative disribution function (CDF). Using the statsmodels package, we can estimate the CDF giving us the empirical cumulative distribution function (ECDF):\n\nfrom statsmodels.distributions.empirical_distribution import ECDF\n\necdf = ECDF(quakes.query('magType == \"ml\"').mag)\nplt.plot(ecdf.x, ecdf.y)\n\n# axis labels (we will cover this in chapter 6)\nplt.xlabel('mag') # add x-axis label \nplt.ylabel('cumulative probability') # add y-axis label\n\n# add title (we will cover this in chapter 6)\nplt.title('ECDF of earthquake magnitude with magType ml')\n\nText(0.5, 1.0, 'ECDF of earthquake magnitude with magType ml')\n\n\n\n\n\n\n\n\n\nThis ECDF tells us the probability of getting an earthquake with magnitude of 3 or less using the ml scale is 98%:\n\nfrom statsmodels.distributions.empirical_distribution import ECDF\n\necdf = ECDF(quakes.query('magType == \"ml\"').mag)\nplt.plot(ecdf.x, ecdf.y)\n\n# formatting below will all be covered in chapter 6\n# axis labels\nplt.xlabel('mag') # add x-axis label \nplt.ylabel('cumulative probability') # add y-axis label\n\n# add reference lines for interpreting the ECDF for mag <= 3 \nplt.plot(\n [3, 3], [0, .98], '--k', \n [-1.5, 3], [0.98, 0.98], '--k', alpha=0.4\n)\n\n# set axis ranges\nplt.ylim(0, None)\nplt.xlim(-1.25, None)\n\n# add a title\nplt.title('P(mag <= 3) = 98%')\n\nText(0.5, 1.0, 'P(mag <= 3) = 98%')\n\n\n\n\n\n\n\n\n\n\n\n\nTo make box plots with pandas, we pass kind='box' to the plot() method:\n\nfb.iloc[:,:4].plot(kind='box', title='Facebook OHLC Prices Box Plot')\nplt.ylabel('price ($)') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'price ($)')\n\n\n\n\n\n\n\n\n\nIf we pass in notch=True, we get a notched box plot. The notch represents a 95% confidence interval around the median, which can be helpful when comparing differences. For an introduction to interpreting a notched box plot, see this Google sites page and this Towards Data Science article.\n\nfb.iloc[:,:4].plot(kind='box', title='Facebook OHLC Prices Box Plot', notch=True)\nplt.ylabel('price ($)') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'price ($)')\n\n\n\n\n\n\n\n\n\nThis can also be combined with a call to groupby():\n\nfb.assign(\n volume_bin=pd.cut(fb.volume, 3, labels=['low', 'med', 'high'])\n).groupby('volume_bin').boxplot(\n column=['open', 'high', 'low', 'close'],\n layout=(1, 3), figsize=(12, 3)\n)\nplt.suptitle('Facebook OHLC Box Plots by Volume Traded', y=1.1)\n\nText(0.5, 1.1, 'Facebook OHLC Box Plots by Volume Traded')\n\n\n\n\n\n\n\n\n\nWe can use this to see the distribution of magnitudes across the different measurement methods for earthquakes:\n\nquakes[['mag', 'magType']].groupby('magType').boxplot(\n figsize=(15, 8), subplots=False\n)\nplt.title('Earthquake Magnitude Box Plots by magType')\nplt.ylabel('magnitude') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'magnitude')" + }, + { + "objectID": "notebooks/plotting_with_pandas.html#counts-and-frequencies", + "href": "notebooks/plotting_with_pandas.html#counts-and-frequencies", + "title": "Plotting with Pandas", + "section": "", + "text": "Passing kind='barh' gives us horizontal bars while kind='bar' gives us vertical ones. Let’s use horizontal bars to look at the top 15 places for earthquakes in our data:\n\nquakes.parsed_place.value_counts().iloc[14::-1,].plot(\n kind='barh', figsize=(10, 5),\n title='Top 15 Places for Earthquakes '\n '(September 18, 2018 - October 13, 2018)'\n)\nplt.xlabel('earthquakes') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'earthquakes')\n\n\n\n\n\n\n\n\n\nWe also have data on whether earthquakes were accompanied by tsunamis. Let’s see what the top places for tsunamis are:\n\nquakes.groupby('parsed_place').tsunami.sum().sort_values().iloc[-10:,].plot(\n kind='barh', figsize=(10, 5), \n title='Top 10 Places for Tsunamis '\n '(September 18, 2018 - October 13, 2018)'\n)\nplt.xlabel('tsunamis') # label the x-axis (discussed in chapter 6)\n\nText(0.5, 0, 'tsunamis')\n\n\n\n\n\n\n\n\n\nSeeing that Indonesia is the top place for tsunamis during the time period we are looking at, we may want to look how many earthquakes and tsunamis Indonesia gets on a daily basis. We could show this as a line plot or with bars; since we don’t want to interpolate, we will use bars here:\n\nindonesia_quakes = quakes.query('parsed_place == \"Indonesia\"').assign(\n time=lambda x: pd.to_datetime(x.time, unit='ms'),\n earthquake=1\n).set_index('time').resample('1D').sum()\n\n# format the datetimes in the index for the x-axis\nindonesia_quakes.index = indonesia_quakes.index.strftime('%b\\n%d')\n\nindonesia_quakes.plot(\n y=['earthquake', 'tsunami'], kind='bar', figsize=(15, 3), \n rot=0, label=['earthquakes', 'tsunamis'], \n title='Earthquakes and Tsunamis in Indonesia '\n '(September 18, 2018 - October 13, 2018)'\n)\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('date')\nplt.ylabel('count')\n\nC:\\Users\\gpower\\AppData\\Local\\Temp\\ipykernel_13112\\3940988219.py:4: FutureWarning: The default value of numeric_only in DataFrameGroupBy.sum is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.\n ).set_index('time').resample('1D').sum()\n\n\nText(0, 0.5, 'count')\n\n\n\n\n\n\n\n\n\n\n\n\n\nquakes.groupby(['parsed_place', 'tsunami']).mag.count()\\\n .unstack().apply(lambda x: x / x.sum(), axis=1)\\\n .rename(columns={0: 'no', 1: 'yes'})\\\n .sort_values('yes', ascending=False)[7::-1]\\\n .plot.barh(\n title='Frequency of a tsunami accompanying an earthquake'\n )\n\n# move legend to the right of the plot\nplt.legend(title='tsunami?', bbox_to_anchor=(1, 0.65))\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('percentage of earthquakes')\nplt.ylabel('')\n\nText(0, 0.5, '')\n\n\n\n\n\n\n\n\n\nUsing the kind arugment for vertical bars when the labels for each bar are shorter:\n\nquakes.magType.value_counts().plot(\n kind='bar', title='Earthquakes Recorded per magType', rot=0\n)\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('magType')\nplt.ylabel('earthquakes')\n\nText(0, 0.5, 'earthquakes')\n\n\n\n\n\n\n\n\n\n\n\n\n\npivot = quakes.assign(\n mag_bin=lambda x: np.floor(x.mag)\n).pivot_table(\n index='mag_bin', columns='magType', values='mag', aggfunc='count'\n)\npivot.plot.bar(\n stacked=True, rot=0, ylabel='earthquakes', \n title='Earthquakes by integer magnitude and magType'\n)\n\n\n\n\n\n\n\n\n\n\nPlot the percentages to be better able to see the different magTypes.\n\nnormalized_pivot = pivot.fillna(0).apply(lambda x: x / x.sum(), axis=1)\nax = normalized_pivot.plot.bar(\n stacked=True, rot=0, figsize=(10, 5),\n title='Percentage of earthquakes by integer magnitude for each magType'\n)\nax.legend(bbox_to_anchor=(1, 0.8)) # move legend to the right of the plot\nplt.ylabel('percentage') # label the y-axis (discussed in chapter 6)\n\nText(0, 0.5, 'percentage')\n\n\n\n\n\n\n\n\n\nWe can also create horizontal stacked bars and do so using groupby() and unstack():\n\nquakes.groupby(['parsed_place', 'tsunami']).mag.count()\\\n .unstack().apply(lambda x: x / x.sum(), axis=1)\\\n .rename(columns={0: 'no', 1: 'yes'})\\\n .sort_values('yes', ascending=False)[7::-1]\\\n .plot.barh(\n title='Frequency of a tsunami accompanying an earthquake', \n stacked=True\n )\n\n# move legend to the right of the plot\nplt.legend(title='tsunami?', bbox_to_anchor=(1, 0.65))\n\n# label the axes (discussed in chapter 6)\nplt.xlabel('percentage of earthquakes')\nplt.ylabel('')\n\nText(0, 0.5, '')" + }, + { + "objectID": "notebooks/one_hot_encoding.html", + "href": "notebooks/one_hot_encoding.html", + "title": "One-Hot Encoding", + "section": "", + "text": "It’s useful for feeding categorical data into machine-learning algorithms since integers are computationally less expensive than strings.\n\nimport pandas as pd\nprint(pd.__version__)\n\n2.0.3\n\n\n\ndisengagements = pd.read_excel(\"../data/cassi-autonomous-shuttle/autonomous_shuttle_disengagement.xlsx\",usecols=[\"Incident Datetime\", \"Location\",\"Weather\",\"Vehicle Speed in Miles per Hour\", \"Initiated by\",\"Cause\"], parse_dates=True)\ndisengagements\n\n\n\n\n\n\n\n\nIncident Datetime\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\n\n\n\n\n0\n2023-03-07T10:00:00-05:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n\n\n1\n2023-03-07T14:00:00-05:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n\n\n2\n2023-03-07T14:30:00-05:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n\n\n3\n2023-03-07T15:15:00-05:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n\n\n4\n2023-03-08T10:00:00-05:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n\n\n...\n...\n...\n...\n...\n...\n...\n\n\n174\n2023-06-01T16:00:00-04:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n\n\n175\n2023-06-02T10:32:00-04:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n\n\n176\n2023-06-02T10:35:00-04:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n\n\n177\n2023-06-02T10:44:00-04:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n\n\n178\n2023-06-02T11:01:00-04:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n\n\n\n\n179 rows × 6 columns\n\n\n\n\ndisengagements.dtypes\n\nIncident Datetime object\nLocation object\nWeather object\nVehicle Speed in Miles per Hour int64\nInitiated by object\nCause object\ndtype: object\n\n\n\ndisengagements['Incident Datetime'] = pd.to_datetime(disengagements['Incident Datetime'], utc=True)\ndisengagements['Initiated by'] = disengagements['Initiated by'].astype('category')\ndisengagements['Cause'] = disengagements['Cause'].astype('category')\ndisengagements.dtypes\n\nIncident Datetime datetime64[ns, UTC]\nLocation object\nWeather object\nVehicle Speed in Miles per Hour int64\nInitiated by category\nCause category\ndtype: object\n\n\n\ndisengagements = disengagements.assign(week_of_year = disengagements['Incident Datetime'].dt.isocalendar().week, week_of_pilot = lambda x: disengagements['Incident Datetime'].dt.isocalendar().week - 9)\ndisengagements\n\n\n\n\n\n\n\n\nIncident Datetime\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\nweek_of_year\nweek_of_pilot\n\n\n\n\n0\n2023-03-07 15:00:00+00:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n10\n1\n\n\n1\n2023-03-07 19:00:00+00:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2\n2023-03-07 19:30:00+00:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n3\n2023-03-07 20:15:00+00:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n10\n1\n\n\n4\n2023-03-08 15:00:00+00:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n10\n1\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n174\n2023-06-01 20:00:00+00:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n22\n13\n\n\n175\n2023-06-02 14:32:00+00:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n22\n13\n\n\n176\n2023-06-02 14:35:00+00:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n22\n13\n\n\n177\n2023-06-02 14:44:00+00:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n22\n13\n\n\n178\n2023-06-02 15:01:00+00:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n22\n13\n\n\n\n\n179 rows × 8 columns\n\n\n\n\ndisengagements['Cause']\n\n0 Fault Code/Error Code\n1 Station Blocked\n2 Station Blocked\n3 Station Blocked\n4 Shuttle Manually Deviated from Approved Path\n ... \n174 Signal Loss\n175 Station Blocked\n176 Station Blocked\n177 Obstacle Detection\n178 Signalized Intersection\nName: Cause, Length: 179, dtype: category\nCategories (9, object): ['Fault Code/Error Code', 'Obstacle Detection', 'Other Road Users', 'Priority Zone', ..., 'Signal Loss', 'Signalized Intersection', 'Station Blocked', 'Vegetation']\n\n\n\ndisengagements['Cause'].cat.categories\n\nIndex(['Fault Code/Error Code', 'Obstacle Detection', 'Other Road Users',\n 'Priority Zone', 'Shuttle Manually Deviated from Approved Path',\n 'Signal Loss', 'Signalized Intersection', 'Station Blocked',\n 'Vegetation'],\n dtype='object')\n\n\n\ndisengagements_datetime_is_index = disengagements.set_index('Incident Datetime')\ndisengagements_datetime_is_index\n\n\n\n\n\n\n\n\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\nweek_of_year\nweek_of_pilot\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 15:00:00+00:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n10\n1\n\n\n2023-03-07 19:00:00+00:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 19:30:00+00:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 20:15:00+00:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-08 15:00:00+00:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n10\n1\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 20:00:00+00:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n22\n13\n\n\n2023-06-02 14:32:00+00:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 14:35:00+00:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 14:44:00+00:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n22\n13\n\n\n2023-06-02 15:01:00+00:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n22\n13\n\n\n\n\n179 rows × 7 columns\n\n\n\n\ndisengagements_datetime_is_index.index=disengagements_datetime_is_index.index.tz_convert(tz='US/Eastern')\ndisengagements_datetime_is_index\n\n\n\n\n\n\n\n\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\nweek_of_year\nweek_of_pilot\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n10\n1\n\n\n2023-03-07 14:00:00-05:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 14:30:00-05:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 15:15:00-05:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-08 10:00:00-05:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n10\n1\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n22\n13\n\n\n2023-06-02 10:32:00-04:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 10:35:00-04:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 10:44:00-04:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n22\n13\n\n\n2023-06-02 11:01:00-04:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n22\n13\n\n\n\n\n179 rows × 7 columns\n\n\n\n\ndisengagements_datetime_is_index.dtypes\n\nLocation object\nWeather object\nVehicle Speed in Miles per Hour int64\nInitiated by category\nCause category\nweek_of_year UInt32\nweek_of_pilot UInt32\ndtype: object\n\n\n\none_hot = disengagements_datetime_is_index.Weather.str.get_dummies(sep=';')\none_hot\n\n\n\n\n\n\n\n\nCloudy\nPartly Cloudy\nRain\nSunny\nWindy\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:32:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 11:01:00-04:00\n0\n0\n0\n1\n0\n\n\n\n\n179 rows × 5 columns\n\n\n\n\none_hot.columns = 'Weather_' + one_hot.columns\none_hot\n\n\n\n\n\n\n\n\nWeather_Cloudy\nWeather_Partly Cloudy\nWeather_Rain\nWeather_Sunny\nWeather_Windy\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:32:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 11:01:00-04:00\n0\n0\n0\n1\n0\n\n\n\n\n179 rows × 5 columns\n\n\n\n\none_hot_cause = disengagements_datetime_is_index.Cause.str.get_dummies()\none_hot_cause.columns = 'Cause_' + one_hot_cause.columns\none_hot_cause\n\n\n\n\n\n\n\n\nCause_Fault Code/Error Code\nCause_Obstacle Detection\nCause_Other Road Users\nCause_Priority Zone\nCause_Shuttle Manually Deviated from Approved Path\nCause_Signal Loss\nCause_Signalized Intersection\nCause_Station Blocked\nCause_Vegetation\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n1\n0\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-03-07 14:00:00-05:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n0\n0\n0\n0\n1\n0\n0\n0\n0\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n0\n0\n0\n0\n0\n1\n0\n0\n0\n\n\n2023-06-02 10:32:00-04:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n0\n1\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-06-02 11:01:00-04:00\n0\n0\n0\n0\n0\n0\n1\n0\n0\n\n\n\n\n179 rows × 9 columns\n\n\n\n\ndisengagements_datetime_is_index = disengagements_datetime_is_index.drop(['Weather', 'Initiated by', 'Cause'], axis=1)\ncassi_data_one_hot_encoded = pd.concat([disengagements_datetime_is_index, one_hot, one_hot_cause], axis=1)\ncassi_data_one_hot_encoded\n\n\n\n\n\n\n\n\nLocation\nVehicle Speed in Miles per Hour\nweek_of_year\nweek_of_pilot\nWeather_Cloudy\nWeather_Partly Cloudy\nWeather_Rain\nWeather_Sunny\nWeather_Windy\nCause_Fault Code/Error Code\nCause_Obstacle Detection\nCause_Other Road Users\nCause_Priority Zone\nCause_Shuttle Manually Deviated from Approved Path\nCause_Signal Loss\nCause_Signalized Intersection\nCause_Station Blocked\nCause_Vegetation\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n35.7849964, -78.8268094\n2\n10\n1\n0\n0\n0\n1\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-03-07 14:00:00-05:00\n35.7847312, -78.8245051\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n35.7824658, -78.8244159\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n35.7824658, -78.8244159\n4\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n35.7852558, -78.8273737\n2\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n1\n0\n0\n0\n0\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n35.783456, -78.821639\n5\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n1\n0\n0\n0\n\n\n2023-06-02 10:32:00-04:00\n35.7819145, -78.8235603\n4\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n35.7813188, -78.8256601\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n35.7847325, -78.824496\n4\n22\n13\n0\n0\n0\n1\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-06-02 11:01:00-04:00\n35.7841086, -78.8261962\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n1\n0\n0\n\n\n\n\n179 rows × 18 columns\n\n\n\n\ncassi_data_one_hot_encoded.index = cassi_data_one_hot_encoded.index.tz_convert(tz='UTC')\n\n\ncassi_data_one_hot_encoded\n\n\n\n\n\n\n\n\nLocation\nVehicle Speed in Miles per Hour\nweek_of_year\nweek_of_pilot\nWeather_Cloudy\nWeather_Partly Cloudy\nWeather_Rain\nWeather_Sunny\nWeather_Windy\nCause_Fault Code/Error Code\nCause_Obstacle Detection\nCause_Other Road Users\nCause_Priority Zone\nCause_Shuttle Manually Deviated from Approved Path\nCause_Signal Loss\nCause_Signalized Intersection\nCause_Station Blocked\nCause_Vegetation\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 15:00:00+00:00\n35.7849964, -78.8268094\n2\n10\n1\n0\n0\n0\n1\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-03-07 19:00:00+00:00\n35.7847312, -78.8245051\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 19:30:00+00:00\n35.7824658, -78.8244159\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 20:15:00+00:00\n35.7824658, -78.8244159\n4\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-08 15:00:00+00:00\n35.7852558, -78.8273737\n2\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n1\n0\n0\n0\n0\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 20:00:00+00:00\n35.783456, -78.821639\n5\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n1\n0\n0\n0\n\n\n2023-06-02 14:32:00+00:00\n35.7819145, -78.8235603\n4\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 14:35:00+00:00\n35.7813188, -78.8256601\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 14:44:00+00:00\n35.7847325, -78.824496\n4\n22\n13\n0\n0\n0\n1\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-06-02 15:01:00+00:00\n35.7841086, -78.8261962\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n1\n0\n0\n\n\n\n\n179 rows × 18 columns" + }, + { + "objectID": "notebooks/one_hot_encoding.html#why-bother-with-one-hot-encoding", + "href": "notebooks/one_hot_encoding.html#why-bother-with-one-hot-encoding", + "title": "One-Hot Encoding", + "section": "", + "text": "It’s useful for feeding categorical data into machine-learning algorithms since integers are computationally less expensive than strings.\n\nimport pandas as pd\nprint(pd.__version__)\n\n2.0.3\n\n\n\ndisengagements = pd.read_excel(\"../data/cassi-autonomous-shuttle/autonomous_shuttle_disengagement.xlsx\",usecols=[\"Incident Datetime\", \"Location\",\"Weather\",\"Vehicle Speed in Miles per Hour\", \"Initiated by\",\"Cause\"], parse_dates=True)\ndisengagements\n\n\n\n\n\n\n\n\nIncident Datetime\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\n\n\n\n\n0\n2023-03-07T10:00:00-05:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n\n\n1\n2023-03-07T14:00:00-05:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n\n\n2\n2023-03-07T14:30:00-05:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n\n\n3\n2023-03-07T15:15:00-05:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n\n\n4\n2023-03-08T10:00:00-05:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n\n\n...\n...\n...\n...\n...\n...\n...\n\n\n174\n2023-06-01T16:00:00-04:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n\n\n175\n2023-06-02T10:32:00-04:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n\n\n176\n2023-06-02T10:35:00-04:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n\n\n177\n2023-06-02T10:44:00-04:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n\n\n178\n2023-06-02T11:01:00-04:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n\n\n\n\n179 rows × 6 columns\n\n\n\n\ndisengagements.dtypes\n\nIncident Datetime object\nLocation object\nWeather object\nVehicle Speed in Miles per Hour int64\nInitiated by object\nCause object\ndtype: object\n\n\n\ndisengagements['Incident Datetime'] = pd.to_datetime(disengagements['Incident Datetime'], utc=True)\ndisengagements['Initiated by'] = disengagements['Initiated by'].astype('category')\ndisengagements['Cause'] = disengagements['Cause'].astype('category')\ndisengagements.dtypes\n\nIncident Datetime datetime64[ns, UTC]\nLocation object\nWeather object\nVehicle Speed in Miles per Hour int64\nInitiated by category\nCause category\ndtype: object\n\n\n\ndisengagements = disengagements.assign(week_of_year = disengagements['Incident Datetime'].dt.isocalendar().week, week_of_pilot = lambda x: disengagements['Incident Datetime'].dt.isocalendar().week - 9)\ndisengagements\n\n\n\n\n\n\n\n\nIncident Datetime\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\nweek_of_year\nweek_of_pilot\n\n\n\n\n0\n2023-03-07 15:00:00+00:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n10\n1\n\n\n1\n2023-03-07 19:00:00+00:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2\n2023-03-07 19:30:00+00:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n3\n2023-03-07 20:15:00+00:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n10\n1\n\n\n4\n2023-03-08 15:00:00+00:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n10\n1\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n174\n2023-06-01 20:00:00+00:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n22\n13\n\n\n175\n2023-06-02 14:32:00+00:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n22\n13\n\n\n176\n2023-06-02 14:35:00+00:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n22\n13\n\n\n177\n2023-06-02 14:44:00+00:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n22\n13\n\n\n178\n2023-06-02 15:01:00+00:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n22\n13\n\n\n\n\n179 rows × 8 columns\n\n\n\n\ndisengagements['Cause']\n\n0 Fault Code/Error Code\n1 Station Blocked\n2 Station Blocked\n3 Station Blocked\n4 Shuttle Manually Deviated from Approved Path\n ... \n174 Signal Loss\n175 Station Blocked\n176 Station Blocked\n177 Obstacle Detection\n178 Signalized Intersection\nName: Cause, Length: 179, dtype: category\nCategories (9, object): ['Fault Code/Error Code', 'Obstacle Detection', 'Other Road Users', 'Priority Zone', ..., 'Signal Loss', 'Signalized Intersection', 'Station Blocked', 'Vegetation']\n\n\n\ndisengagements['Cause'].cat.categories\n\nIndex(['Fault Code/Error Code', 'Obstacle Detection', 'Other Road Users',\n 'Priority Zone', 'Shuttle Manually Deviated from Approved Path',\n 'Signal Loss', 'Signalized Intersection', 'Station Blocked',\n 'Vegetation'],\n dtype='object')\n\n\n\ndisengagements_datetime_is_index = disengagements.set_index('Incident Datetime')\ndisengagements_datetime_is_index\n\n\n\n\n\n\n\n\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\nweek_of_year\nweek_of_pilot\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 15:00:00+00:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n10\n1\n\n\n2023-03-07 19:00:00+00:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 19:30:00+00:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 20:15:00+00:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-08 15:00:00+00:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n10\n1\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 20:00:00+00:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n22\n13\n\n\n2023-06-02 14:32:00+00:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 14:35:00+00:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 14:44:00+00:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n22\n13\n\n\n2023-06-02 15:01:00+00:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n22\n13\n\n\n\n\n179 rows × 7 columns\n\n\n\n\ndisengagements_datetime_is_index.index=disengagements_datetime_is_index.index.tz_convert(tz='US/Eastern')\ndisengagements_datetime_is_index\n\n\n\n\n\n\n\n\nLocation\nWeather\nVehicle Speed in Miles per Hour\nInitiated by\nCause\nweek_of_year\nweek_of_pilot\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n35.7849964, -78.8268094\nSunny;\n2\nOperator\nFault Code/Error Code\n10\n1\n\n\n2023-03-07 14:00:00-05:00\n35.7847312, -78.8245051\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 14:30:00-05:00\n35.7824658, -78.8244159\nSunny;\n5\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-07 15:15:00-05:00\n35.7824658, -78.8244159\nSunny;\n4\nOperator\nStation Blocked\n10\n1\n\n\n2023-03-08 10:00:00-05:00\n35.7852558, -78.8273737\nSunny;\n2\nOperator\nShuttle Manually Deviated from Approved Path\n10\n1\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n35.783456, -78.821639\nSunny;\n5\nOperator\nSignal Loss\n22\n13\n\n\n2023-06-02 10:32:00-04:00\n35.7819145, -78.8235603\nSunny;\n4\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 10:35:00-04:00\n35.7813188, -78.8256601\nSunny;\n3\nOperator\nStation Blocked\n22\n13\n\n\n2023-06-02 10:44:00-04:00\n35.7847325, -78.824496\nSunny;\n4\nOperator\nObstacle Detection\n22\n13\n\n\n2023-06-02 11:01:00-04:00\n35.7841086, -78.8261962\nSunny;\n3\nOperator\nSignalized Intersection\n22\n13\n\n\n\n\n179 rows × 7 columns\n\n\n\n\ndisengagements_datetime_is_index.dtypes\n\nLocation object\nWeather object\nVehicle Speed in Miles per Hour int64\nInitiated by category\nCause category\nweek_of_year UInt32\nweek_of_pilot UInt32\ndtype: object\n\n\n\none_hot = disengagements_datetime_is_index.Weather.str.get_dummies(sep=';')\none_hot\n\n\n\n\n\n\n\n\nCloudy\nPartly Cloudy\nRain\nSunny\nWindy\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:32:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 11:01:00-04:00\n0\n0\n0\n1\n0\n\n\n\n\n179 rows × 5 columns\n\n\n\n\none_hot.columns = 'Weather_' + one_hot.columns\none_hot\n\n\n\n\n\n\n\n\nWeather_Cloudy\nWeather_Partly Cloudy\nWeather_Rain\nWeather_Sunny\nWeather_Windy\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:00:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n0\n0\n0\n1\n0\n\n\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:32:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n0\n0\n0\n1\n0\n\n\n2023-06-02 11:01:00-04:00\n0\n0\n0\n1\n0\n\n\n\n\n179 rows × 5 columns\n\n\n\n\none_hot_cause = disengagements_datetime_is_index.Cause.str.get_dummies()\none_hot_cause.columns = 'Cause_' + one_hot_cause.columns\none_hot_cause\n\n\n\n\n\n\n\n\nCause_Fault Code/Error Code\nCause_Obstacle Detection\nCause_Other Road Users\nCause_Priority Zone\nCause_Shuttle Manually Deviated from Approved Path\nCause_Signal Loss\nCause_Signalized Intersection\nCause_Station Blocked\nCause_Vegetation\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n1\n0\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-03-07 14:00:00-05:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n0\n0\n0\n0\n1\n0\n0\n0\n0\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n0\n0\n0\n0\n0\n1\n0\n0\n0\n\n\n2023-06-02 10:32:00-04:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n0\n1\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-06-02 11:01:00-04:00\n0\n0\n0\n0\n0\n0\n1\n0\n0\n\n\n\n\n179 rows × 9 columns\n\n\n\n\ndisengagements_datetime_is_index = disengagements_datetime_is_index.drop(['Weather', 'Initiated by', 'Cause'], axis=1)\ncassi_data_one_hot_encoded = pd.concat([disengagements_datetime_is_index, one_hot, one_hot_cause], axis=1)\ncassi_data_one_hot_encoded\n\n\n\n\n\n\n\n\nLocation\nVehicle Speed in Miles per Hour\nweek_of_year\nweek_of_pilot\nWeather_Cloudy\nWeather_Partly Cloudy\nWeather_Rain\nWeather_Sunny\nWeather_Windy\nCause_Fault Code/Error Code\nCause_Obstacle Detection\nCause_Other Road Users\nCause_Priority Zone\nCause_Shuttle Manually Deviated from Approved Path\nCause_Signal Loss\nCause_Signalized Intersection\nCause_Station Blocked\nCause_Vegetation\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 10:00:00-05:00\n35.7849964, -78.8268094\n2\n10\n1\n0\n0\n0\n1\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-03-07 14:00:00-05:00\n35.7847312, -78.8245051\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 14:30:00-05:00\n35.7824658, -78.8244159\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 15:15:00-05:00\n35.7824658, -78.8244159\n4\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-08 10:00:00-05:00\n35.7852558, -78.8273737\n2\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n1\n0\n0\n0\n0\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 16:00:00-04:00\n35.783456, -78.821639\n5\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n1\n0\n0\n0\n\n\n2023-06-02 10:32:00-04:00\n35.7819145, -78.8235603\n4\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:35:00-04:00\n35.7813188, -78.8256601\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 10:44:00-04:00\n35.7847325, -78.824496\n4\n22\n13\n0\n0\n0\n1\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-06-02 11:01:00-04:00\n35.7841086, -78.8261962\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n1\n0\n0\n\n\n\n\n179 rows × 18 columns\n\n\n\n\ncassi_data_one_hot_encoded.index = cassi_data_one_hot_encoded.index.tz_convert(tz='UTC')\n\n\ncassi_data_one_hot_encoded\n\n\n\n\n\n\n\n\nLocation\nVehicle Speed in Miles per Hour\nweek_of_year\nweek_of_pilot\nWeather_Cloudy\nWeather_Partly Cloudy\nWeather_Rain\nWeather_Sunny\nWeather_Windy\nCause_Fault Code/Error Code\nCause_Obstacle Detection\nCause_Other Road Users\nCause_Priority Zone\nCause_Shuttle Manually Deviated from Approved Path\nCause_Signal Loss\nCause_Signalized Intersection\nCause_Station Blocked\nCause_Vegetation\n\n\nIncident Datetime\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n2023-03-07 15:00:00+00:00\n35.7849964, -78.8268094\n2\n10\n1\n0\n0\n0\n1\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-03-07 19:00:00+00:00\n35.7847312, -78.8245051\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 19:30:00+00:00\n35.7824658, -78.8244159\n5\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-07 20:15:00+00:00\n35.7824658, -78.8244159\n4\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-03-08 15:00:00+00:00\n35.7852558, -78.8273737\n2\n10\n1\n0\n0\n0\n1\n0\n0\n0\n0\n0\n1\n0\n0\n0\n0\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n2023-06-01 20:00:00+00:00\n35.783456, -78.821639\n5\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n1\n0\n0\n0\n\n\n2023-06-02 14:32:00+00:00\n35.7819145, -78.8235603\n4\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 14:35:00+00:00\n35.7813188, -78.8256601\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n0\n1\n0\n\n\n2023-06-02 14:44:00+00:00\n35.7847325, -78.824496\n4\n22\n13\n0\n0\n0\n1\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n\n\n2023-06-02 15:01:00+00:00\n35.7841086, -78.8261962\n3\n22\n13\n0\n0\n0\n1\n0\n0\n0\n0\n0\n0\n0\n1\n0\n0\n\n\n\n\n179 rows × 18 columns" + }, + { + "objectID": "notebooks/introduction_to_data_analysis.html", + "href": "notebooks/introduction_to_data_analysis.html", + "title": "Introduction to Data Analysis", + "section": "", + "text": "This notebook serves as a summary of the fundamentals. For a Python crash-course/refresher, work through the python_101.ipynb notebook.\n\n\n\nimport sys\nsys.path.append('../src/')\nsys.dont_write_bytecode = True\n\nimport stats_viz\n\n\n\n\nWhen conducting a data analysis, we will move back and forth between four main processes:\n\nData Collection: Every analysis starts with collecting data. We can collect data from a variety of sources, including databases, APIs, flat files, and the Internet.\nData Wrangling: After we have our data, we need to prepare it for our analysis. This may involve reshaping it, changing data types, handling missing values, and/or aggregating it.\nExploratory Data Analysis (EDA): We can use visualizations to explore our data and summarize it. During this time, we will also begin exploring the data by looking at its structure, format, and summary statistics.\nDrawing Conclusions: After we have thoroughly explored our data, we can try to draw conclusions or model it.\n\n\n\n\nAs this is an overview of statistics, we will discuss some concepts. By no means is this exhaustive.\n\n\nSome resampling (sampling from the sample) techniques you will see: - simple random sampling: pick with a random number generator - stratified random sampling: randomly pick preserving the proportion of groups in the data - bootstrapping: sampling with replacement (more info: YouTube video and Wikipedia article)\n\n\n\nWe use descriptive statistics to describe the data. The data we work with is usually a sample taken from the population. The statistics we will discuss here are referred to as sample statistics because they are calculated on the sample and can be used as estimators for the population parameters.\n\n\nThree common ways to describe the central tendency of a distribution are mean, median, and mode. ##### Mean The sample mean is an estimator for the population mean (\\(\\mu\\)) and is defined as:\n\\[\\bar{x} = \\frac{\\sum_{1}^{n} x_i}{n}\\] ##### Median The median represents the 50th percentile of our data; this means that 50% of the values are greater than the median and 50% are less than the median. It is calculated by taking the middle value from an ordered list of values.\n\n\nThe mode is the most common value in the data. We can use it to describe categorical data or, for continuous data, the shape of the distribution:\n\nax = stats_viz.different_modal_plots()\n\n\n\n\n\n\n\n\n\n\n\n\nMeasures of spread tell us how the data is dispersed; this will indicate how thin (low dispersion) or wide (very spread out) our distribution is.\n\n\nThe range is the distance between the smallest value (minimum) and the largest value (maximum):\n\\[range = max(X) - min(X)\\]\n\n\n\nThe variance describes how far apart observations are spread out from their average value (the mean). When calculating the sample variance, we divide by n - 1 instead of n to account for using the sample mean (\\(\\bar{x}\\)):\n\\[s^2 = \\frac{\\sum_{1}^{n} (x_i - \\bar{x})^2}{n - 1}\\]\nThis is referred to as Bessel’s correction and is applied to get an unbiased estimator of the population variance.\nNote that this will be in units-squared of whatever was being measured.\n\n\n\nThe standard deviation is the square root of the variance, giving us a measure in the same units as our data. The sample standard deviation is calculated as follows:\n\\[s = \\sqrt{\\frac{\\sum_{1}^{n} (x_i - \\bar{x})^2}{n - 1}} = \\sqrt{s^2}\\]\n\nax = stats_viz.effect_of_std_dev()\n\n\n\n\n\n\n\n\nNote that \\(\\sigma^2\\) is the population variance and \\(\\sigma\\) is the population standard deviation.\n\n\n\nThe coefficient of variation (CV) gives us a unitless ratio of the standard deviation to the mean. Since, it has no units we can compare dispersion across datasets:\n\\[CV = \\frac{s}{\\bar{x}}\\]\n\n\n\nThe interquartile range (IQR) gives us the spread of data around the median and quantifies how much dispersion we have in the middle 50% of our distribution:\n\\[IQR = Q_3 - Q_1\\]\n\n\n\nThe quartile coefficient of dispersion also is a unitless statistic for comparing datasets. However, it uses the median as the measure of center. It is calculated by dividing the semi-quartile range (half the IQR) by the midhinge (midpoint between the first and third quartiles):\n\\[QCD = \\frac{\\frac{Q_3 - Q_1}{2}}{\\frac{Q_1 + Q_3}{2}} = \\frac{Q_3 - Q_1}{Q_3 + Q_1}\\]\n\n\n\n\nThe 5-number summary provides 5 descriptive statistics that summarize our data:\n\n\n\n\nQuartile\nStatistic\nPercentile\n\n\n\n\n1.\n\\(Q_0\\)\nminimum\n\\(0^{th}\\)\n\n\n2.\n\\(Q_1\\)\nN/A\n\\(25^{th}\\)\n\n\n3.\n\\(Q_2\\)\nmedian\n\\(50^{th}\\)\n\n\n4.\n\\(Q_3\\)\nN/A\n\\(75^{th}\\)\n\n\n5.\n\\(Q_4\\)\nmaximum\n\\(100^{th}\\)\n\n\n\nThis summary can be visualized using a box plot (also called box-and-whisker plot). The box has an upper bound of \\(Q_3\\) and a lower bound of \\(Q_1\\). The median will be a line somewhere in this box. The whiskers extend from the box towards the minimum/maximum. For our purposes, they will extend to \\(Q_3 + 1.5 \\times IQR\\) and \\(Q_1 - 1.5 \\times IQR\\) and anything beyond will be represented as individual points for outliers:\n\nax = stats_viz.example_boxplot()\n\n\n\n\n\n\n\n\nThe box plot doesn’t show us how the data is distributed within the quartiles. To get a better sense of the distribution, we can use a histogram, which will show us the amount of observations that fall into equal-width bins. We can vary the number of bins to use, but be aware that this can change our impression of what the distribution appears to be:\n\nax = stats_viz.example_histogram()\n\n\n\n\n\n\n\n\nWe can also visualize the distribution using a kernel density estimate (KDE). This will estimate the probability density function (PDF). This function shows how probability is distributed over the values. Higher values of the PDF mean higher likelihoods:\n\nax = stats_viz.example_kde()\n\n\n\n\n\n\n\n\nNote that both the KDE and histogram estimate the distribution:\n\nax = stats_viz.hist_and_kde()\n\n\n\n\n\n\n\n\nSkewed distributions have more observations on one side. The mean will be less than the median with negative skew, while the opposite is true of positive skew:\n\nax = stats_viz.skew_examples()\n\n\n\n\n\n\n\n\nWe can use the cumulative distribution function (CDF) to find probabilities of getting values within a certain range. The CDF is the integral of the PDF:\n\\[CDF = F(x) = \\int_{-\\infty}^{x} f(t) dt\\]\nNote that \\(f(t)\\) is the PDF and \\(\\int_{-\\infty}^{\\infty} f(t) dt = 1\\).\nThe probability of the random variable \\(X\\) being less than or equal to the specific value of \\(x\\) is denoted as \\(P(X ≤ x)\\). Note that for a continuous random variable the probability of it being exactly \\(x\\) is zero.\nLet’s look at the estimate of the CDF from the sample data we used for the box plot, called the empirical cumulative distribution function (ECDF):\n\nax = stats_viz.cdf_example()\n\n\n\n\n\n\n\n\nWe can find any range we want if we use some algebra as in the rightmost subplot above.\n\n\n\n\nGaussian (normal) distribution: looks like a bell curve and is parameterized by its mean (μ) and standard deviation (σ). Many things in nature happen to follow the normal distribution, like heights. Note that testing if a distribution is normal is not trivial. Written as \\(N(\\mu, \\sigma)\\).\nPoisson distribution: discrete distribution that is often used to model arrivals. Parameterized by its mean, lambda (λ). Written as \\(Pois(\\lambda)\\).\nExponential distribution: can be used to model the time between arrivals. Parameterized by its mean, lambda (λ). Written as \\(Exp(\\lambda)\\).\nUniform distribution: places equal likelihood on each value within its bounds (a and b). We often use this for random number generation. Written as \\(U(a, b)\\).\nBernoulli distribution: When we pick a random number to simulate a single success/failure outcome, it is called a Bernoulli trial. This is parameterized by the probability of success (p). Written as \\(Bernoulli(p)\\).\nBinomial distribution: When we run the same experiment n times, the total number of successes is then a binomial random variable. Written as \\(B(n, p)\\).\n\nWe can visualize both discrete and continuous distributions; however, discrete distributions give us a probability mass function (PMF) instead of a PDF:\n\nax = stats_viz.common_dists()\n\n\n\n\n\n\n\n\n\n\n\nIn order to compare variables from different distributions, we would have to scale the data, which we could do with the range by using min-max scaling:\n\\[x_{scaled}=\\frac{x - min(X)}{range(X)}\\]\nAnother way is to use a Z-score to standardize the data:\n\\[z_i = \\frac{x_i - \\bar{x}}{s}\\]\n\n\n\nThe covariance is a statistic for quantifying the relationship between variables by showing how one variable changes with respect to another (also referred to as their joint variance):\n\\[cov(X, Y) = E[(X-E[X])(Y-E[Y])]\\]\nE[X] is the expectation of the random variable X (its long-run average).\nThe sign of the covariance gives us the direction of the relationship, but we need the magnitude as well. For that, we calculate the Pearson correlation coefficient (\\(\\rho\\)):\n\\[\\rho_{X, Y} = \\frac{cov(X, Y)}{s_X s_Y}\\]\nExamples:\n\nax = stats_viz.correlation_coefficient_examples()\n\n\n\n\n\n\n\n\nFrom left to right: no correlation, weak negative correlation, strong positive correlation, and nearly perfect negative correlation.\nOften, it is more informative to use scatter plots to check for relationships between variables. This is because the correlation may be strong, but the relationship may not be linear:\n\nax = stats_viz.non_linear_relationships()\n\n\n\n\n\n\n\n\nRemember, correlation does not imply causation. While we may find a correlation between X and Y, it does not mean that X causes Y or Y causes X. It is possible there is some Z that causes both or that X causes some intermediary event that causes Y — it could even be a coincidence. Be sure to check out Tyler Vigen’s Spurious Correlations blog for some interesting correlations.\n\n\n\nNot only can our correlation coefficients be misleading, but so can summary statistics. Anscombe’s quartet is a collection of four different datasets that have identical summary statistics and correlation coefficients, however, when plotted, it is obvious they are not similar:\n\nax = stats_viz.anscombes_quartet()\n\n\n\n\n\n\n\n\nAnother example of this is the Datasaurus Dozen:\n\nax = stats_viz.datasaurus_dozen()\n\n\n\n\n\n\n\n\n\n\n\n\nSay our favorite ice cream shop has asked us to help predict how many ice creams they can expect to sell on a given day. They are convinced that the temperature outside has strong influence on their sales, so they collected data on the number of ice creams sold at a given temperature. We agree to help them, and the first thing we do is make a scatter plot of the data they gave us:\n\nax = stats_viz.example_scatter_plot()\n\n\n\n\n\n\n\n\nWe can observe an upward trend in the scatter plot: more ice creams are sold at higher temperatures. In order to help out the ice cream shop, though, we need to find a way to make predictions from this data. We can use a technique called regression to model the relationship between temperature and ice cream sales with an equation:\n\nax = stats_viz.example_regression()\n\n\n\n\n\n\n\n\nWe can use the resulting equation to make predictions for the number of ice creams sold at various temperatures. However, we must keep in mind if we are interpolating or extrapolating. If the temperature value we are using for prediction is within the range of the original data we used to build our regression model, then we are interpolating (solid portion of the red line). On the other hand, if the temperature is beyond the values in the original data, we are extrapolating, which is very dangerous, since we can’t assume the pattern continues indefinitely in each direction (dotted portion of the line). Extremely hot temperatures may cause people to stay inside, meaning no ice creams will be sold, while the equation indicates record-high sales.\nForecasting is a type of prediction for time series. In a process called time series decomposition, time series is decomposed into a trend component, a seasonality component, and a cyclical component. These components can be combined in an additive or multiplicative fashion:\n\nax = stats_viz.time_series_decomposition_example()\n\n\n\n\n\n\n\n\nThe trend component describes the behavior of the time series in the long-term without accounting for the seasonal or cyclical effects. Using the trend, we can make broad statements about the time series in the long-run, such as: the population of Earth is increasing or the value of a stock is stagnating. Seasonality of a time series explains the systematic and calendar-related movements of a time series. For example, the number of ice cream trucks on the streets of New York City is high in the summer and drops to nothing in the winter; this pattern repeats every year regardless of whether the actual amount each summer is the same. Lastly, the cyclical component accounts for anything else unexplained or irregular with the time series; this could be something like a hurricane driving the number of ice cream trucks down in the short-term because it isn’t safe to be outside. This component is difficult to anticipate with a forecast due to its unexpected nature.\nWhen making models to forecast time series, some common methods include ARIMA-family methods and exponential smoothing. ARIMA stands for autoregressive (AR), integrated (I), moving average (MA). Autoregressive models take advantage of the fact that an observation at time \\(t\\) is correlated to a previous observation, for example at time \\(t - 1\\). Note that not all time series are autoregressive. The integrated component concerns the differenced data, or the change in the data from one time to another. Lastly, the moving average component uses a sliding window to average the last \\(x\\) observations where \\(x\\) is the length of the sliding window.\nThe moving average puts equal weight on each time period in the past involved in the calculation. In practice, this isn’t always a realistic expectation of our data. Sometimes all past values are important, but they vary in their influence on future data points. For these cases, we can use exponential smoothing, which allows us to put more weight on more recent values and less weight on values further away from what we are predicting.\n\n\n\nInferential statistics deals with inferring or deducing things from the sample data we have in order to make statements about the population as a whole. Before doing so, we need to know whether we conducted an observational study or an experiment. An observational study can’t be used to determine causation because we can’t control for everything. An experiment on the other hand is controlled.\nRemember that the sample statistics we discussed earlier are estimators for the population parameters. Our estimators need confidence intervals, which provide a point estimate and a margin of error around it. This is the range that the true population parameter will be in at a certain confidence level. At the 95% confidence level, 95% of the confidence intervals calculated from random samples of the population contain the true population parameter.\nWe also have the option of using hypothesis testing. First, we define a null hypothesis (say the true population mean is 0), then we determine a significance level (1 - confidence level), which is the probability of rejecting the null hypothesis when it is true. Our result is statistically significant if the value for the null hypothesis is outside the confidence interval. More info." + }, + { + "objectID": "notebooks/introduction_to_data_analysis.html#setup", + "href": "notebooks/introduction_to_data_analysis.html#setup", + "title": "Introduction to Data Analysis", + "section": "", + "text": "import sys\nsys.path.append('../src/')\nsys.dont_write_bytecode = True\n\nimport stats_viz" + }, + { + "objectID": "notebooks/introduction_to_data_analysis.html#fundamentals-of-data-analysis", + "href": "notebooks/introduction_to_data_analysis.html#fundamentals-of-data-analysis", + "title": "Introduction to Data Analysis", + "section": "", + "text": "When conducting a data analysis, we will move back and forth between four main processes:\n\nData Collection: Every analysis starts with collecting data. We can collect data from a variety of sources, including databases, APIs, flat files, and the Internet.\nData Wrangling: After we have our data, we need to prepare it for our analysis. This may involve reshaping it, changing data types, handling missing values, and/or aggregating it.\nExploratory Data Analysis (EDA): We can use visualizations to explore our data and summarize it. During this time, we will also begin exploring the data by looking at its structure, format, and summary statistics.\nDrawing Conclusions: After we have thoroughly explored our data, we can try to draw conclusions or model it." + }, + { + "objectID": "notebooks/introduction_to_data_analysis.html#statistical-foundations", + "href": "notebooks/introduction_to_data_analysis.html#statistical-foundations", + "title": "Introduction to Data Analysis", + "section": "", + "text": "As this is an overview of statistics, we will discuss some concepts. By no means is this exhaustive.\n\n\nSome resampling (sampling from the sample) techniques you will see: - simple random sampling: pick with a random number generator - stratified random sampling: randomly pick preserving the proportion of groups in the data - bootstrapping: sampling with replacement (more info: YouTube video and Wikipedia article)\n\n\n\nWe use descriptive statistics to describe the data. The data we work with is usually a sample taken from the population. The statistics we will discuss here are referred to as sample statistics because they are calculated on the sample and can be used as estimators for the population parameters.\n\n\nThree common ways to describe the central tendency of a distribution are mean, median, and mode. ##### Mean The sample mean is an estimator for the population mean (\\(\\mu\\)) and is defined as:\n\\[\\bar{x} = \\frac{\\sum_{1}^{n} x_i}{n}\\] ##### Median The median represents the 50th percentile of our data; this means that 50% of the values are greater than the median and 50% are less than the median. It is calculated by taking the middle value from an ordered list of values.\n\n\nThe mode is the most common value in the data. We can use it to describe categorical data or, for continuous data, the shape of the distribution:\n\nax = stats_viz.different_modal_plots()\n\n\n\n\n\n\n\n\n\n\n\n\nMeasures of spread tell us how the data is dispersed; this will indicate how thin (low dispersion) or wide (very spread out) our distribution is.\n\n\nThe range is the distance between the smallest value (minimum) and the largest value (maximum):\n\\[range = max(X) - min(X)\\]\n\n\n\nThe variance describes how far apart observations are spread out from their average value (the mean). When calculating the sample variance, we divide by n - 1 instead of n to account for using the sample mean (\\(\\bar{x}\\)):\n\\[s^2 = \\frac{\\sum_{1}^{n} (x_i - \\bar{x})^2}{n - 1}\\]\nThis is referred to as Bessel’s correction and is applied to get an unbiased estimator of the population variance.\nNote that this will be in units-squared of whatever was being measured.\n\n\n\nThe standard deviation is the square root of the variance, giving us a measure in the same units as our data. The sample standard deviation is calculated as follows:\n\\[s = \\sqrt{\\frac{\\sum_{1}^{n} (x_i - \\bar{x})^2}{n - 1}} = \\sqrt{s^2}\\]\n\nax = stats_viz.effect_of_std_dev()\n\n\n\n\n\n\n\n\nNote that \\(\\sigma^2\\) is the population variance and \\(\\sigma\\) is the population standard deviation.\n\n\n\nThe coefficient of variation (CV) gives us a unitless ratio of the standard deviation to the mean. Since, it has no units we can compare dispersion across datasets:\n\\[CV = \\frac{s}{\\bar{x}}\\]\n\n\n\nThe interquartile range (IQR) gives us the spread of data around the median and quantifies how much dispersion we have in the middle 50% of our distribution:\n\\[IQR = Q_3 - Q_1\\]\n\n\n\nThe quartile coefficient of dispersion also is a unitless statistic for comparing datasets. However, it uses the median as the measure of center. It is calculated by dividing the semi-quartile range (half the IQR) by the midhinge (midpoint between the first and third quartiles):\n\\[QCD = \\frac{\\frac{Q_3 - Q_1}{2}}{\\frac{Q_1 + Q_3}{2}} = \\frac{Q_3 - Q_1}{Q_3 + Q_1}\\]\n\n\n\n\nThe 5-number summary provides 5 descriptive statistics that summarize our data:\n\n\n\n\nQuartile\nStatistic\nPercentile\n\n\n\n\n1.\n\\(Q_0\\)\nminimum\n\\(0^{th}\\)\n\n\n2.\n\\(Q_1\\)\nN/A\n\\(25^{th}\\)\n\n\n3.\n\\(Q_2\\)\nmedian\n\\(50^{th}\\)\n\n\n4.\n\\(Q_3\\)\nN/A\n\\(75^{th}\\)\n\n\n5.\n\\(Q_4\\)\nmaximum\n\\(100^{th}\\)\n\n\n\nThis summary can be visualized using a box plot (also called box-and-whisker plot). The box has an upper bound of \\(Q_3\\) and a lower bound of \\(Q_1\\). The median will be a line somewhere in this box. The whiskers extend from the box towards the minimum/maximum. For our purposes, they will extend to \\(Q_3 + 1.5 \\times IQR\\) and \\(Q_1 - 1.5 \\times IQR\\) and anything beyond will be represented as individual points for outliers:\n\nax = stats_viz.example_boxplot()\n\n\n\n\n\n\n\n\nThe box plot doesn’t show us how the data is distributed within the quartiles. To get a better sense of the distribution, we can use a histogram, which will show us the amount of observations that fall into equal-width bins. We can vary the number of bins to use, but be aware that this can change our impression of what the distribution appears to be:\n\nax = stats_viz.example_histogram()\n\n\n\n\n\n\n\n\nWe can also visualize the distribution using a kernel density estimate (KDE). This will estimate the probability density function (PDF). This function shows how probability is distributed over the values. Higher values of the PDF mean higher likelihoods:\n\nax = stats_viz.example_kde()\n\n\n\n\n\n\n\n\nNote that both the KDE and histogram estimate the distribution:\n\nax = stats_viz.hist_and_kde()\n\n\n\n\n\n\n\n\nSkewed distributions have more observations on one side. The mean will be less than the median with negative skew, while the opposite is true of positive skew:\n\nax = stats_viz.skew_examples()\n\n\n\n\n\n\n\n\nWe can use the cumulative distribution function (CDF) to find probabilities of getting values within a certain range. The CDF is the integral of the PDF:\n\\[CDF = F(x) = \\int_{-\\infty}^{x} f(t) dt\\]\nNote that \\(f(t)\\) is the PDF and \\(\\int_{-\\infty}^{\\infty} f(t) dt = 1\\).\nThe probability of the random variable \\(X\\) being less than or equal to the specific value of \\(x\\) is denoted as \\(P(X ≤ x)\\). Note that for a continuous random variable the probability of it being exactly \\(x\\) is zero.\nLet’s look at the estimate of the CDF from the sample data we used for the box plot, called the empirical cumulative distribution function (ECDF):\n\nax = stats_viz.cdf_example()\n\n\n\n\n\n\n\n\nWe can find any range we want if we use some algebra as in the rightmost subplot above.\n\n\n\n\nGaussian (normal) distribution: looks like a bell curve and is parameterized by its mean (μ) and standard deviation (σ). Many things in nature happen to follow the normal distribution, like heights. Note that testing if a distribution is normal is not trivial. Written as \\(N(\\mu, \\sigma)\\).\nPoisson distribution: discrete distribution that is often used to model arrivals. Parameterized by its mean, lambda (λ). Written as \\(Pois(\\lambda)\\).\nExponential distribution: can be used to model the time between arrivals. Parameterized by its mean, lambda (λ). Written as \\(Exp(\\lambda)\\).\nUniform distribution: places equal likelihood on each value within its bounds (a and b). We often use this for random number generation. Written as \\(U(a, b)\\).\nBernoulli distribution: When we pick a random number to simulate a single success/failure outcome, it is called a Bernoulli trial. This is parameterized by the probability of success (p). Written as \\(Bernoulli(p)\\).\nBinomial distribution: When we run the same experiment n times, the total number of successes is then a binomial random variable. Written as \\(B(n, p)\\).\n\nWe can visualize both discrete and continuous distributions; however, discrete distributions give us a probability mass function (PMF) instead of a PDF:\n\nax = stats_viz.common_dists()\n\n\n\n\n\n\n\n\n\n\n\nIn order to compare variables from different distributions, we would have to scale the data, which we could do with the range by using min-max scaling:\n\\[x_{scaled}=\\frac{x - min(X)}{range(X)}\\]\nAnother way is to use a Z-score to standardize the data:\n\\[z_i = \\frac{x_i - \\bar{x}}{s}\\]\n\n\n\nThe covariance is a statistic for quantifying the relationship between variables by showing how one variable changes with respect to another (also referred to as their joint variance):\n\\[cov(X, Y) = E[(X-E[X])(Y-E[Y])]\\]\nE[X] is the expectation of the random variable X (its long-run average).\nThe sign of the covariance gives us the direction of the relationship, but we need the magnitude as well. For that, we calculate the Pearson correlation coefficient (\\(\\rho\\)):\n\\[\\rho_{X, Y} = \\frac{cov(X, Y)}{s_X s_Y}\\]\nExamples:\n\nax = stats_viz.correlation_coefficient_examples()\n\n\n\n\n\n\n\n\nFrom left to right: no correlation, weak negative correlation, strong positive correlation, and nearly perfect negative correlation.\nOften, it is more informative to use scatter plots to check for relationships between variables. This is because the correlation may be strong, but the relationship may not be linear:\n\nax = stats_viz.non_linear_relationships()\n\n\n\n\n\n\n\n\nRemember, correlation does not imply causation. While we may find a correlation between X and Y, it does not mean that X causes Y or Y causes X. It is possible there is some Z that causes both or that X causes some intermediary event that causes Y — it could even be a coincidence. Be sure to check out Tyler Vigen’s Spurious Correlations blog for some interesting correlations.\n\n\n\nNot only can our correlation coefficients be misleading, but so can summary statistics. Anscombe’s quartet is a collection of four different datasets that have identical summary statistics and correlation coefficients, however, when plotted, it is obvious they are not similar:\n\nax = stats_viz.anscombes_quartet()\n\n\n\n\n\n\n\n\nAnother example of this is the Datasaurus Dozen:\n\nax = stats_viz.datasaurus_dozen()\n\n\n\n\n\n\n\n\n\n\n\n\nSay our favorite ice cream shop has asked us to help predict how many ice creams they can expect to sell on a given day. They are convinced that the temperature outside has strong influence on their sales, so they collected data on the number of ice creams sold at a given temperature. We agree to help them, and the first thing we do is make a scatter plot of the data they gave us:\n\nax = stats_viz.example_scatter_plot()\n\n\n\n\n\n\n\n\nWe can observe an upward trend in the scatter plot: more ice creams are sold at higher temperatures. In order to help out the ice cream shop, though, we need to find a way to make predictions from this data. We can use a technique called regression to model the relationship between temperature and ice cream sales with an equation:\n\nax = stats_viz.example_regression()\n\n\n\n\n\n\n\n\nWe can use the resulting equation to make predictions for the number of ice creams sold at various temperatures. However, we must keep in mind if we are interpolating or extrapolating. If the temperature value we are using for prediction is within the range of the original data we used to build our regression model, then we are interpolating (solid portion of the red line). On the other hand, if the temperature is beyond the values in the original data, we are extrapolating, which is very dangerous, since we can’t assume the pattern continues indefinitely in each direction (dotted portion of the line). Extremely hot temperatures may cause people to stay inside, meaning no ice creams will be sold, while the equation indicates record-high sales.\nForecasting is a type of prediction for time series. In a process called time series decomposition, time series is decomposed into a trend component, a seasonality component, and a cyclical component. These components can be combined in an additive or multiplicative fashion:\n\nax = stats_viz.time_series_decomposition_example()\n\n\n\n\n\n\n\n\nThe trend component describes the behavior of the time series in the long-term without accounting for the seasonal or cyclical effects. Using the trend, we can make broad statements about the time series in the long-run, such as: the population of Earth is increasing or the value of a stock is stagnating. Seasonality of a time series explains the systematic and calendar-related movements of a time series. For example, the number of ice cream trucks on the streets of New York City is high in the summer and drops to nothing in the winter; this pattern repeats every year regardless of whether the actual amount each summer is the same. Lastly, the cyclical component accounts for anything else unexplained or irregular with the time series; this could be something like a hurricane driving the number of ice cream trucks down in the short-term because it isn’t safe to be outside. This component is difficult to anticipate with a forecast due to its unexpected nature.\nWhen making models to forecast time series, some common methods include ARIMA-family methods and exponential smoothing. ARIMA stands for autoregressive (AR), integrated (I), moving average (MA). Autoregressive models take advantage of the fact that an observation at time \\(t\\) is correlated to a previous observation, for example at time \\(t - 1\\). Note that not all time series are autoregressive. The integrated component concerns the differenced data, or the change in the data from one time to another. Lastly, the moving average component uses a sliding window to average the last \\(x\\) observations where \\(x\\) is the length of the sliding window.\nThe moving average puts equal weight on each time period in the past involved in the calculation. In practice, this isn’t always a realistic expectation of our data. Sometimes all past values are important, but they vary in their influence on future data points. For these cases, we can use exponential smoothing, which allows us to put more weight on more recent values and less weight on values further away from what we are predicting.\n\n\n\nInferential statistics deals with inferring or deducing things from the sample data we have in order to make statements about the population as a whole. Before doing so, we need to know whether we conducted an observational study or an experiment. An observational study can’t be used to determine causation because we can’t control for everything. An experiment on the other hand is controlled.\nRemember that the sample statistics we discussed earlier are estimators for the population parameters. Our estimators need confidence intervals, which provide a point estimate and a margin of error around it. This is the range that the true population parameter will be in at a certain confidence level. At the 95% confidence level, 95% of the confidence intervals calculated from random samples of the population contain the true population parameter.\nWe also have the option of using hypothesis testing. First, we define a null hypothesis (say the true population mean is 0), then we determine a significance level (1 - confidence level), which is the probability of rejecting the null hypothesis when it is true. Our result is statistically significant if the value for the null hypothesis is outside the confidence interval. More info." + }, + { + "objectID": "notebooks/intro_to_plotly_express.html", + "href": "notebooks/intro_to_plotly_express.html", + "title": "", + "section": "", + "text": "import plotly.express as px\n\ndf = px.data.gapminder().query(\"year==2007\")\ndf.columns\n\nIndex(['country', 'continent', 'year', 'lifeExp', 'pop', 'gdpPercap',\n 'iso_alpha', 'iso_num'],\n dtype='object')\ndf.describe()\n\n\n\n\n\n\n\n\nyear\nlifeExp\npop\ngdpPercap\niso_num\n\n\n\n\ncount\n142.0\n142.000000\n1.420000e+02\n142.000000\n142.000000\n\n\nmean\n2007.0\n67.007423\n4.402122e+07\n11680.071820\n425.880282\n\n\nstd\n0.0\n12.073021\n1.476214e+08\n12859.937337\n249.111541\n\n\nmin\n2007.0\n39.613000\n1.995790e+05\n277.551859\n4.000000\n\n\n25%\n2007.0\n57.160250\n4.508034e+06\n1624.842248\n209.500000\n\n\n50%\n2007.0\n71.935500\n1.051753e+07\n6124.371109\n410.000000\n\n\n75%\n2007.0\n76.413250\n3.121004e+07\n18008.835640\n636.000000\n\n\nmax\n2007.0\n82.603000\n1.318683e+09\n49357.190170\n894.000000\npx.strip(df, x='lifeExp', hover_name=\"country\")\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.strip(df, x='lifeExp', color=\"continent\", hover_name=\"country\")\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.histogram(df, x='lifeExp', color=\"continent\", hover_name=\"country\")\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.histogram(df, x='lifeExp', color=\"continent\", hover_name=\"country\", marginal=\"rug\")\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.histogram(df, x='lifeExp', y=\"pop\", color=\"continent\", hover_name=\"country\", marginal=\"rug\")\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.histogram(df, x='lifeExp', y=\"pop\", color=\"continent\", hover_name=\"country\", marginal=\"rug\", facet_col=\"continent\")\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.bar(df, color='lifeExp', x=\"pop\", y=\"continent\", hover_name=\"country\")\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.sunburst(df, color='lifeExp', values=\"pop\", path=[\"continent\", \"country\"], hover_name=\"country\", height=500)\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.treemap(df, color='lifeExp', values=\"pop\", path=[\"continent\", \"country\"], hover_name=\"country\", height=500)\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.choropleth(df, color='lifeExp', locations=\"iso_alpha\", hover_name=\"country\", height=500)\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.scatter(df, x=\"gdpPercap\", y='lifeExp', hover_name=\"country\", height=500)\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\npx.scatter(df, x=\"gdpPercap\", y='lifeExp', hover_name=\"country\", color=\"continent\",size=\"pop\", height=500)\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\nWe can see that the curve follows a logarithmic path, so make log_x=True to straighten out the line to view the relationships in an easier manner. In the graph below we can view the monotic and nonmonotonic relationships in the dataset.\npx.scatter(df, x=\"gdpPercap\", y='lifeExp', hover_name=\"country\", color=\"continent\",size=\"pop\", size_max=60, log_x=True, height=500)\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\nfig = px.scatter(df, x=\"gdpPercap\", y='lifeExp', hover_name=\"country\", color=\"continent\",size=\"pop\", size_max=60, log_x=True, height=500)\nThis will allow you to inspect the values for each of these cells, unfortunately this is a great deal easier to see in JupyterLab.\nfig.show(\"json\")\n\nUnable to display output for mime type(s): application/json\nimport plotly.express as px\n\ndf = px.data.gapminder().query(\"year == 2007\")\n\nfig = px.scatter(df, x=\"gdpPercap\",y=\"lifeExp\", color=\"continent\", log_x=True, size=\"pop\", size_max=60,\n hover_name=\"country\", height=600, width=1000, template=\"simple_white\", \n color_discrete_sequence=px.colors.qualitative.G10,\n title=\"Health vs Wealth 2007\",\n labels=dict(\n continent=\"Continent\", pop=\"Population\",\n gdpPercap=\"GDP per Capita (US$, price-adjusted)\", \n lifeExp=\"Life Expectancy (years)\"))\n\nfig.update_layout(font_family=\"Rockwell\",\n legend=dict(orientation=\"h\", title=\"\", y=1.1, x=1, xanchor=\"right\", yanchor=\"bottom\"))\nfig.update_xaxes(tickprefix=\"$\", range=[2,5], dtick=1)\nfig.update_yaxes(range=[30,90])\nfig.add_hline((df[\"lifeExp\"]*df[\"pop\"]).sum()/df[\"pop\"].sum(), line_width=1, line_dash=\"dot\")\nfig.add_vline((df[\"gdpPercap\"]*df[\"pop\"]).sum()/df[\"pop\"].sum(), line_width=1, line_dash=\"dot\")\nfig.show()\n\n# fig.write_image(\"gapminder_2007.svg\") # static export\n# fig.write_html(\"gapminder_2007.html\") # interactive export\n# fig.write_json(\"gapminder_2007.json\") # serialized export\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json" + }, + { + "objectID": "notebooks/intro_to_plotly_express.html#animations-in-plotly-express", + "href": "notebooks/intro_to_plotly_express.html#animations-in-plotly-express", + "title": "", + "section": "Animations in Plotly Express", + "text": "Animations in Plotly Express\n\ndf_animation = px.data.gapminder()\n\nanim_fig = px.scatter(df_animation, x=\"gdpPercap\", y=\"lifeExp\",\n title=\"Health vs Wealth from 1952 to 2007\",\n labels=dict(continent=\"Continent\", pop=\"Population\", gdpPercap=\"GDP per Capita (US$, price-adjusted)\", lifeExp=\"Life Expectancy (years)\"),\n animation_frame=\"year\", animation_group=\"country\",\n size=\"pop\",\n color=\"continent\",\n hover_name=\"country\",\n height=600,width=1000,\n template=\"simple_white\",\n color_discrete_sequence=px.colors.qualitative.G10,\n log_x=True,\n size_max=60,\n range_x=[100,100000],\n range_y=[25,90])\n\nanim_fig.update_layout(font_family=\"Rockwell\",\n legend=dict(orientation=\"h\", title=\"\", y=1.1, x=1, xanchor=\"right\", yanchor=\"bottom\"))\nanim_fig.update_xaxes(tickprefix=\"$\", range=[2,5], dtick=1)\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\nanim_fig.write_html(\"gapminder_animation.html\", auto_play=False) # You're able to export this animation.\n\n\npx.defaults.height=600\n\n\nimport plotly.express as px\n\nz = [[.1, .3, .5, .7, .9],\n [1, .8, .6, .4, .2],\n [.2, 0, .5, .7, .9],\n [.9, .8, .4, .2, 0],\n [.3, .4, .5, .7, 1]]\n\nfig = px.imshow(z, text_auto=True)\nfig.show()\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\nimport plotly.express as px\ndf = px.data.wind()\nfig = px.bar_polar(df, r=\"frequency\", theta=\"direction\", height=600,\n color=\"strength\", template=\"plotly_dark\",\n color_discrete_sequence= px.colors.sequential.Plasma_r)\nfig.show()\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\ndf = px.data.iris()\nfig = px.parallel_coordinates(df, color=\"species_id\", labels={\"species_id\": \"Species\",\n \"sepal_width\": \"Sepal Width\", \"sepal_length\": \"Sepal Length\",\n \"petal_width\": \"Petal Width\", \"petal_length\": \"Petal Length\", },\n color_continuous_scale=px.colors.diverging.Tealrose, color_continuous_midpoint=2)\nfig.show()\n\nc:\\Users\\gpower\\Anaconda3\\envs\\cary_dev\\lib\\site-packages\\plotly\\express\\_core.py:279: FutureWarning:\n\niteritems is deprecated and will be removed in a future version. Use .items instead.\n\n\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\ndf = px.data.tips()\nfig = px.parallel_categories(df, color=\"size\", color_continuous_scale=px.colors.sequential.Inferno)\nfig.show()\n\nc:\\Users\\gpower\\Anaconda3\\envs\\cary_dev\\lib\\site-packages\\plotly\\express\\_core.py:279: FutureWarning:\n\niteritems is deprecated and will be removed in a future version. Use .items instead.\n\n\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\ndf = px.data.iris()\nfig = px.parallel_coordinates(df, color=\"species_id\", labels={\"species_id\": \"Species\",\n \"sepal_width\": \"Sepal Width\", \"sepal_length\": \"Sepal Length\",\n \"petal_width\": \"Petal Width\", \"petal_length\": \"Petal Length\", },\n color_continuous_scale=px.colors.diverging.Tealrose, color_continuous_midpoint=2)\nfig.show()\n\nc:\\Users\\gpower\\Anaconda3\\envs\\cary_dev\\lib\\site-packages\\plotly\\express\\_core.py:279: FutureWarning:\n\niteritems is deprecated and will be removed in a future version. Use .items instead.\n\n\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\ndf = px.data.tips()\nfig = px.parallel_categories(df, color=\"size\", color_continuous_scale=px.colors.sequential.Inferno)\nfig.show()\n\nc:\\Users\\gpower\\Anaconda3\\envs\\cary_dev\\lib\\site-packages\\plotly\\express\\_core.py:279: FutureWarning:\n\niteritems is deprecated and will be removed in a future version. Use .items instead.\n\n\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\ndf = px.data.election()\nfig = px.scatter_ternary(df, a=\"Joly\", b=\"Coderre\", c=\"Bergeron\", color=\"winner\", size=\"total\", hover_name=\"district\",\n size_max=15, color_discrete_map = {\"Joly\": \"blue\", \"Bergeron\": \"green\", \"Coderre\":\"red\"} )\nfig.show()\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json\n\n\n\ndf = px.data.election()\nfig = px.scatter_3d(df, x=\"Joly\", y=\"Coderre\", z=\"Bergeron\", color=\"winner\", size=\"total\", hover_name=\"district\",\n symbol=\"result\", color_discrete_map = {\"Joly\": \"blue\", \"Bergeron\": \"green\", \"Coderre\":\"red\"})\nfig.show()\n\nUnable to display output for mime type(s): application/vnd.plotly.v1+json" + }, + { + "objectID": "notebooks/handling_data_issues.html", + "href": "notebooks/handling_data_issues.html", + "title": "Handling duplicate, missing, or invalid data", + "section": "", + "text": "In this notebook, we will using daily weather data that was taken from the National Centers for Environmental Information (NCEI) API and altered to introduce many common problems faced when working with data.\nNote: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.\n\n\n\nData meanings: - PRCP: precipitation in millimeters - SNOW: snowfall in millimeters - SNWD: snow depth in millimeters - TMAX: maximum daily temperature in Celsius - TMIN: minimum daily temperature in Celsius - TOBS: temperature at time of observation in Celsius - WESF: water equivalent of snow in millimeters\nSome important facts to get our bearings: - According to the National Weather Service, the coldest temperature ever recorded in Central Park was -15°F (-26.1°C) on February 9, 1934: source - The temperature of the Sun’s photosphere is approximately 5,505°C: source\n\n\n\nWe need to import pandas and read in the dirty data to get started:\n\nimport pandas as pd\n\ndf = pd.read_csv('../data/dirty_data.csv')\n\n\n\n\nA good first step is to look at some rows:\n\ndf.head()\n\n\n\n\n\n\n\n\ndate\nstation\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n0\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n1\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n3\n2018-01-02T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\nNaN\nFalse\n\n\n4\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n\n\n\n\n\nLooking at summary statistics can reveal strange or missing values:\n\ndf.describe()\n\nc:\\Users\\gpower\\AppData\\Local\\mambaforge\\envs\\cary_dev\\Lib\\site-packages\\numpy\\lib\\function_base.py:4573: RuntimeWarning: invalid value encountered in subtract\n diff_b_a = subtract(b, a)\n\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\n\n\n\n\ncount\n765.000000\n577.000000\n577.0\n765.000000\n765.000000\n398.000000\n11.000000\n\n\nmean\n5.360392\n4.202773\nNaN\n2649.175294\n-15.914379\n8.632161\n16.290909\n\n\nstd\n10.002138\n25.086077\nNaN\n2744.156281\n24.242849\n9.815054\n9.489832\n\n\nmin\n0.000000\n0.000000\n-inf\n-11.700000\n-40.000000\n-16.100000\n1.800000\n\n\n25%\n0.000000\n0.000000\nNaN\n13.300000\n-40.000000\n0.150000\n8.600000\n\n\n50%\n0.000000\n0.000000\nNaN\n32.800000\n-11.100000\n8.300000\n19.300000\n\n\n75%\n5.800000\n0.000000\nNaN\n5505.000000\n6.700000\n18.300000\n24.900000\n\n\nmax\n61.700000\n229.000000\ninf\n5505.000000\n23.900000\n26.100000\n28.700000\n\n\n\n\n\n\n\nThe info() method can pinpoint missing values and wrong data types:\n\ndf.info()\n\n<class 'pandas.core.frame.DataFrame'>\nRangeIndex: 765 entries, 0 to 764\nData columns (total 10 columns):\n # Column Non-Null Count Dtype \n--- ------ -------------- ----- \n 0 date 765 non-null object \n 1 station 765 non-null object \n 2 PRCP 765 non-null float64\n 3 SNOW 577 non-null float64\n 4 SNWD 577 non-null float64\n 5 TMAX 765 non-null float64\n 6 TMIN 765 non-null float64\n 7 TOBS 398 non-null float64\n 8 WESF 11 non-null float64\n 9 inclement_weather 408 non-null object \ndtypes: float64(7), object(3)\nmemory usage: 59.9+ KB\n\n\nWe can use the isna()/isnull() method of the series to find nulls:\n\ncontain_nulls = df[\n df.SNOW.isna() | df.SNWD.isna() | df.TOBS.isna()\n | df.WESF.isna() | df.inclement_weather.isna()\n]\ncontain_nulls.shape[0]\n\n765\n\n\n\ncontain_nulls.head(10)\n\n\n\n\n\n\n\n\ndate\nstation\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n0\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n1\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n3\n2018-01-02T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\nNaN\nFalse\n\n\n4\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n5\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n6\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n7\n2018-01-04T00:00:00\n?\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n8\n2018-01-04T00:00:00\n?\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n9\n2018-01-05T00:00:00\n?\n0.3\nNaN\nNaN\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n\n\n\n\n\nNote that we can’t check if we have NaN like this:\n\ndf[df.inclement_weather == 'NaN'].shape[0]\n\n0\n\n\nThis is because it is actually np.nan. However, notice this also doesn’t work:\n\nimport numpy as np\ndf[df.inclement_weather == np.nan].shape[0]\n\n0\n\n\nWe have to use one of the methods discussed earlier for this to work:\n\ndf[df.inclement_weather.isna()].shape[0]\n\n357\n\n\nWe can find -inf/inf by comparing to -np.inf/np.inf:\n\ndf[df.SNWD.isin([-np.inf, np.inf])].shape[0]\n\n577\n\n\nRather than do this for each column, we can write a function that will use a dictionary comprehension to check all the columns for us:\n\ndef get_inf_count(df):\n \"\"\"Find the number of inf/-inf values per column in the dataframe\"\"\"\n return {\n col: df[df[col].isin([np.inf, -np.inf])].shape[0] for col in df.columns\n }\n\nget_inf_count(df)\n\n{'date': 0,\n 'station': 0,\n 'PRCP': 0,\n 'SNOW': 0,\n 'SNWD': 577,\n 'TMAX': 0,\n 'TMIN': 0,\n 'TOBS': 0,\n 'WESF': 0,\n 'inclement_weather': 0}\n\n\nBefore we can decide how to handle the infinite values of snow depth, we should look at the summary statistics for snowfall, which forms a big part in determining the snow depth:\n\npd.DataFrame({\n 'np.inf Snow Depth': df[df.SNWD == np.inf].SNOW.describe(),\n '-np.inf Snow Depth': df[df.SNWD == -np.inf].SNOW.describe()\n}).T\n\n\n\n\n\n\n\n\ncount\nmean\nstd\nmin\n25%\n50%\n75%\nmax\n\n\n\n\nnp.inf Snow Depth\n24.0\n101.041667\n74.498018\n13.0\n25.0\n120.5\n152.0\n229.0\n\n\n-np.inf Snow Depth\n553.0\n0.000000\n0.000000\n0.0\n0.0\n0.0\n0.0\n0.0\n\n\n\n\n\n\n\nLet’s now look into the date and station columns. We saw the ? for station earlier, so we know that was the other unique value. However, we see that some dates are present 8 times in the data and we only have 324 days meaning we are also missing days:\n\ndf.describe(include='object')\n\n\n\n\n\n\n\n\ndate\nstation\ninclement_weather\n\n\n\n\ncount\n765\n765\n408\n\n\nunique\n324\n2\n2\n\n\ntop\n2018-07-05T00:00:00\nGHCND:USC00280907\nFalse\n\n\nfreq\n8\n398\n384\n\n\n\n\n\n\n\nWe can use the duplicated() method to find duplicate rows:\n\ndf[df.duplicated()].shape[0]\n\n284\n\n\nThe default for keep is 'first' meaning it won’t show the first row that the duplicated data was seen in; we can pass in False to see it though:\n\ndf[df.duplicated(keep=False)].shape[0]\n\n482\n\n\nWe can also specify the columns to use:\n\ndf[df.duplicated(['date', 'station'])].shape[0]\n\n284\n\n\nLet’s look at a few duplicates. Just in the few values we see here, we know that the top 4 are actually in the data 6 times because by default we aren’t seeing their first occurrence:\n\ndf[df.duplicated()].head()\n\n\n\n\n\n\n\n\ndate\nstation\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n1\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n5\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n6\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n8\n2018-01-04T00:00:00\n?\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n\n\n\n\n\n\n\n\n\n\nSince we know we have NY weather data and noticed we only had two entries for station, we may decide to drop the station column because we are only interested in the weather data. However, when dealing with duplicate data, we need to think of the ramifications of removing it. Notice we only have data for the WESF column when the station is ?:\n\ndf[df.WESF.notna()].station.unique()\n\narray(['?'], dtype=object)\n\n\nIf we determine it won’t impact our analysis, we can use drop_duplicates() to remove them:\n\n# 1. make the date a datetime\ndf.date = pd.to_datetime(df.date)\n\n# 2. save this information for later\nstation_qm_wesf = df[df.station == '?'].drop_duplicates('date').set_index('date').WESF\n\n# 3. sort ? to the bottom\ndf.sort_values('station', ascending=False, inplace=True)\n\n# 4. drop duplicates based on the date column keeping the first occurrence \n# which will be the valid station if it has data\ndf_deduped = df.drop_duplicates('date')\n\n# 5. remove the station column because we are done with it\ndf_deduped = df_deduped.drop(columns='station').set_index('date').sort_index()\n\n# 6. take valid station's WESF and fall back on station ? if it is null\ndf_deduped = df_deduped.assign(\n WESF=lambda x: x.WESF.combine_first(station_qm_wesf)\n)\n\ndf_deduped.shape\n\n(324, 8)\n\n\nHere we used the combine_first() method to coalesce the values to the first non-null entry; this means that if we had data from both stations, we would first take the value provided by the named station and if (and only if) that station was null would we take the value from the station named ?. The following table contains some examples of how this would play out:\n\n\n\nstation GHCND:USC00280907\nstation ?\nresult of combine_first()\n\n\n\n\n1\n17\n1\n\n\n1\nNaN\n1\n\n\nNaN\n17\n17\n\n\nNaN\nNaN\nNaN\n\n\n\nCheck out the 4th row—we have WESF in the correct spot thanks to the index:\n\ndf_deduped.head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\nNaN\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\nNaN\nTrue\n\n\n\n\n\n\n\n\n\n\nWe could drop nulls, replace them with some arbitrary value, or impute them using the surrounding data. Each of these options may have ramifications, so we must choose wisely.\nWe can use dropna() to drop rows where any column has a null value. The default options leave us hardly any data:\n\ndf_deduped.dropna().shape\n\n(4, 8)\n\n\nIf we pass how='all', we can choose to only drop rows where everything is null, but this removes nothing:\n\ndf_deduped.dropna(how='all').shape\n\n(324, 8)\n\n\nWe can use just a subset of columns to determine what to drop with the subset argument:\n\ndf_deduped.dropna(\n how='all', subset=['inclement_weather', 'SNOW', 'SNWD']\n).shape\n\n(293, 8)\n\n\nThis can also be performed along columns, and we can also require a certain number of null values before we drop the data:\n\ndf_deduped.dropna(axis='columns', thresh=df_deduped.shape[0] * .75).columns\n\nIndex(['PRCP', 'SNOW', 'SNWD', 'TMAX', 'TMIN', 'TOBS', 'inclement_weather'], dtype='object')\n\n\nWe can choose to fill in the null values instead with fillna():\n\ndf_deduped.loc[:,'WESF'].fillna(0, inplace=True)\ndf_deduped.head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nAt this point we have done everything we can without distorting the data. We know that we are missing dates, but if we reindex, we don’t know how to fill in the NaN data. With the weather data, we can’t assume because it snowed one day that it will snow the next or that the temperature will be the same. For this reason, note that the next few examples are just for illustrative purposes only—just because we can do something doesn’t mean we should.\nThat being said, let’s try to address some of remaining issues with the temperature data. We know that when TMAX is the temperature of the Sun, it must be because there was no measured value, so let’s replace it with NaN. We will also do so for TMIN which currently uses -40°C for its placeholder when we know that the coldest temperature ever recorded in NYC was -15°F (-26.1°C) on February 9, 1934:\n\ndf_deduped = df_deduped.assign(\n TMAX=lambda x: x.TMAX.replace(5505, np.nan),\n TMIN=lambda x: x.TMIN.replace(-40, np.nan),\n)\n\nWe will also make an assumption that the temperature won’t change drastically day-to-day. Note that this is actually a big assumption, but it will allow us to understand how fillna() works when we provide a strategy through the method parameter. The fillna() method gives us 2 options for the method parameter: - 'ffill' to forward-fill - 'bfill' to back-fill\nNote that 'nearest' is missing because we are not reindexing.\nHere, we will use 'ffill' to show how this works:\n\ndf_deduped.assign(\n TMAX=lambda x: x.TMAX.fillna(method='ffill'),\n TMIN=lambda x: x.TMIN.fillna(method='ffill')\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n-4.4\n-13.9\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nWe can use np.nan_to_num() to turn np.nan into 0 and -np.inf/np.inf into large negative or positive finite numbers:\n\ndf_deduped.assign(\n SNWD=lambda x: np.nan_to_num(x.SNWD)\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-1.797693e+308\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-1.797693e+308\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-1.797693e+308\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\n1.797693e+308\nNaN\nNaN\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\n1.797693e+308\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nDepending on the data we are working with, we can use the clip() method as an alternative to np.nan_to_num(). The clip() method makes it possible to cap values at a specific minimum and/or maximum threshold. Since SNWD can’t be negative, let’s use clip() to enforce a lower bound of zero. To show how the upper bound works, let’s use the value of SNOW:\n\ndf_deduped.assign(\n SNWD=lambda x: x.SNWD.clip(0, x.SNOW)\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n0.0\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n0.0\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n0.0\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\n229.0\nNaN\nNaN\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\n127.0\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nWe can couple fillna() with other types of calculations. Here we replace missing values of TMAX with the median of all TMAX values, TMIN with the median of all TMIN values, and TOBS to the average of the TMAX and TMIN values. Since we place TOBS last, we have access to the imputed values for TMIN and TMAX in the calculation:\n\ndf_deduped.assign(\n TMAX=lambda x: x.TMAX.fillna(x.TMAX.median()),\n TMIN=lambda x: x.TMIN.fillna(x.TMIN.median()),\n # average of TMAX and TMIN\n TOBS=lambda x: x.TOBS.fillna((x.TMAX + x.TMIN) / 2)\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\n14.4\n5.6\n10.0\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n14.4\n5.6\n10.0\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nWe can also use apply() for running the same calculation across columns. For example, let’s fill all missing values with their rolling 7-day median of their values, setting the number of periods required for the calculation to 0 to ensure we don’t introduce more extra NaN values. Rolling calculations will be covered later on, so this is a preview:\n\ndf_deduped.apply(\n # rolling calculations will be covered later on, this is a rolling 7-day median\n # we set min_periods (# of periods required for calculation) to 0 so we always get a result \n lambda x: x.fillna(x.rolling(7, min_periods=0).median())\n).head(10)\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.30\n-16.1\n-12.20\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.40\n-13.9\n-13.30\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n-6.35\n-15.0\n-12.75\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.40\n-13.9\n-13.90\n0.0\nTrue\n\n\n2018-01-06\n0.0\n0.0\n-inf\n-10.00\n-15.6\n-15.00\n0.0\nFalse\n\n\n2018-01-07\n0.0\n0.0\n-inf\n-11.70\n-17.2\n-16.10\n0.0\nFalse\n\n\n2018-01-08\n0.0\n0.0\n-inf\n-7.80\n-16.7\n-8.30\n0.0\nFalse\n\n\n2018-01-10\n0.0\n0.0\n-inf\n5.00\n-7.8\n-7.80\n0.0\nFalse\n\n\n2018-01-11\n0.0\n0.0\n-inf\n4.40\n-7.8\n1.10\n0.0\nFalse\n\n\n\n\n\n\n\nThe last strategy we could try is interpolation with the interpolate() method. We specify the method parameter with the interpolation strategy to use. There are many options, but we will stick with the default of 'linear', which will treat values as evenly spaced and place missing values in the middle of existing ones. We have some missing data, so we will reindex first. Look at January 9th, which we didn’t have before—the values for TMAX, TMIN, and TOBS are the average of values the day prior (January 8th) and the day after (January 10th):\n\ndf_deduped\\\n .reindex(pd.date_range('2018-01-01', '2018-12-31', freq='D'))\\\n .apply(lambda x: x.interpolate())\\\n .head(10)\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.10\n-12.20\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.90\n-13.30\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n-4.4\n-13.90\n-13.60\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.90\n-13.90\n0.0\nTrue\n\n\n2018-01-06\n0.0\n0.0\n-inf\n-10.0\n-15.60\n-15.00\n0.0\nFalse\n\n\n2018-01-07\n0.0\n0.0\n-inf\n-11.7\n-17.20\n-16.10\n0.0\nFalse\n\n\n2018-01-08\n0.0\n0.0\n-inf\n-7.8\n-16.70\n-8.30\n0.0\nFalse\n\n\n2018-01-09\n0.0\n0.0\n-inf\n-1.4\n-12.25\n-8.05\n0.0\nNaN\n\n\n2018-01-10\n0.0\n0.0\n-inf\n5.0\n-7.80\n-7.80\n0.0\nFalse" + }, + { + "objectID": "notebooks/handling_data_issues.html#about-the-data", + "href": "notebooks/handling_data_issues.html#about-the-data", + "title": "Handling duplicate, missing, or invalid data", + "section": "", + "text": "In this notebook, we will using daily weather data that was taken from the National Centers for Environmental Information (NCEI) API and altered to introduce many common problems faced when working with data.\nNote: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one." + }, + { + "objectID": "notebooks/handling_data_issues.html#background-on-the-data", + "href": "notebooks/handling_data_issues.html#background-on-the-data", + "title": "Handling duplicate, missing, or invalid data", + "section": "", + "text": "Data meanings: - PRCP: precipitation in millimeters - SNOW: snowfall in millimeters - SNWD: snow depth in millimeters - TMAX: maximum daily temperature in Celsius - TMIN: minimum daily temperature in Celsius - TOBS: temperature at time of observation in Celsius - WESF: water equivalent of snow in millimeters\nSome important facts to get our bearings: - According to the National Weather Service, the coldest temperature ever recorded in Central Park was -15°F (-26.1°C) on February 9, 1934: source - The temperature of the Sun’s photosphere is approximately 5,505°C: source" + }, + { + "objectID": "notebooks/handling_data_issues.html#setup", + "href": "notebooks/handling_data_issues.html#setup", + "title": "Handling duplicate, missing, or invalid data", + "section": "", + "text": "We need to import pandas and read in the dirty data to get started:\n\nimport pandas as pd\n\ndf = pd.read_csv('../data/dirty_data.csv')" + }, + { + "objectID": "notebooks/handling_data_issues.html#finding-problematic-data", + "href": "notebooks/handling_data_issues.html#finding-problematic-data", + "title": "Handling duplicate, missing, or invalid data", + "section": "", + "text": "A good first step is to look at some rows:\n\ndf.head()\n\n\n\n\n\n\n\n\ndate\nstation\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n0\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n1\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n3\n2018-01-02T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\nNaN\nFalse\n\n\n4\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n\n\n\n\n\nLooking at summary statistics can reveal strange or missing values:\n\ndf.describe()\n\nc:\\Users\\gpower\\AppData\\Local\\mambaforge\\envs\\cary_dev\\Lib\\site-packages\\numpy\\lib\\function_base.py:4573: RuntimeWarning: invalid value encountered in subtract\n diff_b_a = subtract(b, a)\n\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\n\n\n\n\ncount\n765.000000\n577.000000\n577.0\n765.000000\n765.000000\n398.000000\n11.000000\n\n\nmean\n5.360392\n4.202773\nNaN\n2649.175294\n-15.914379\n8.632161\n16.290909\n\n\nstd\n10.002138\n25.086077\nNaN\n2744.156281\n24.242849\n9.815054\n9.489832\n\n\nmin\n0.000000\n0.000000\n-inf\n-11.700000\n-40.000000\n-16.100000\n1.800000\n\n\n25%\n0.000000\n0.000000\nNaN\n13.300000\n-40.000000\n0.150000\n8.600000\n\n\n50%\n0.000000\n0.000000\nNaN\n32.800000\n-11.100000\n8.300000\n19.300000\n\n\n75%\n5.800000\n0.000000\nNaN\n5505.000000\n6.700000\n18.300000\n24.900000\n\n\nmax\n61.700000\n229.000000\ninf\n5505.000000\n23.900000\n26.100000\n28.700000\n\n\n\n\n\n\n\nThe info() method can pinpoint missing values and wrong data types:\n\ndf.info()\n\n<class 'pandas.core.frame.DataFrame'>\nRangeIndex: 765 entries, 0 to 764\nData columns (total 10 columns):\n # Column Non-Null Count Dtype \n--- ------ -------------- ----- \n 0 date 765 non-null object \n 1 station 765 non-null object \n 2 PRCP 765 non-null float64\n 3 SNOW 577 non-null float64\n 4 SNWD 577 non-null float64\n 5 TMAX 765 non-null float64\n 6 TMIN 765 non-null float64\n 7 TOBS 398 non-null float64\n 8 WESF 11 non-null float64\n 9 inclement_weather 408 non-null object \ndtypes: float64(7), object(3)\nmemory usage: 59.9+ KB\n\n\nWe can use the isna()/isnull() method of the series to find nulls:\n\ncontain_nulls = df[\n df.SNOW.isna() | df.SNWD.isna() | df.TOBS.isna()\n | df.WESF.isna() | df.inclement_weather.isna()\n]\ncontain_nulls.shape[0]\n\n765\n\n\n\ncontain_nulls.head(10)\n\n\n\n\n\n\n\n\ndate\nstation\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n0\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n1\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n3\n2018-01-02T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\nNaN\nFalse\n\n\n4\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n5\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n6\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n7\n2018-01-04T00:00:00\n?\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n8\n2018-01-04T00:00:00\n?\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n9\n2018-01-05T00:00:00\n?\n0.3\nNaN\nNaN\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n\n\n\n\n\nNote that we can’t check if we have NaN like this:\n\ndf[df.inclement_weather == 'NaN'].shape[0]\n\n0\n\n\nThis is because it is actually np.nan. However, notice this also doesn’t work:\n\nimport numpy as np\ndf[df.inclement_weather == np.nan].shape[0]\n\n0\n\n\nWe have to use one of the methods discussed earlier for this to work:\n\ndf[df.inclement_weather.isna()].shape[0]\n\n357\n\n\nWe can find -inf/inf by comparing to -np.inf/np.inf:\n\ndf[df.SNWD.isin([-np.inf, np.inf])].shape[0]\n\n577\n\n\nRather than do this for each column, we can write a function that will use a dictionary comprehension to check all the columns for us:\n\ndef get_inf_count(df):\n \"\"\"Find the number of inf/-inf values per column in the dataframe\"\"\"\n return {\n col: df[df[col].isin([np.inf, -np.inf])].shape[0] for col in df.columns\n }\n\nget_inf_count(df)\n\n{'date': 0,\n 'station': 0,\n 'PRCP': 0,\n 'SNOW': 0,\n 'SNWD': 577,\n 'TMAX': 0,\n 'TMIN': 0,\n 'TOBS': 0,\n 'WESF': 0,\n 'inclement_weather': 0}\n\n\nBefore we can decide how to handle the infinite values of snow depth, we should look at the summary statistics for snowfall, which forms a big part in determining the snow depth:\n\npd.DataFrame({\n 'np.inf Snow Depth': df[df.SNWD == np.inf].SNOW.describe(),\n '-np.inf Snow Depth': df[df.SNWD == -np.inf].SNOW.describe()\n}).T\n\n\n\n\n\n\n\n\ncount\nmean\nstd\nmin\n25%\n50%\n75%\nmax\n\n\n\n\nnp.inf Snow Depth\n24.0\n101.041667\n74.498018\n13.0\n25.0\n120.5\n152.0\n229.0\n\n\n-np.inf Snow Depth\n553.0\n0.000000\n0.000000\n0.0\n0.0\n0.0\n0.0\n0.0\n\n\n\n\n\n\n\nLet’s now look into the date and station columns. We saw the ? for station earlier, so we know that was the other unique value. However, we see that some dates are present 8 times in the data and we only have 324 days meaning we are also missing days:\n\ndf.describe(include='object')\n\n\n\n\n\n\n\n\ndate\nstation\ninclement_weather\n\n\n\n\ncount\n765\n765\n408\n\n\nunique\n324\n2\n2\n\n\ntop\n2018-07-05T00:00:00\nGHCND:USC00280907\nFalse\n\n\nfreq\n8\n398\n384\n\n\n\n\n\n\n\nWe can use the duplicated() method to find duplicate rows:\n\ndf[df.duplicated()].shape[0]\n\n284\n\n\nThe default for keep is 'first' meaning it won’t show the first row that the duplicated data was seen in; we can pass in False to see it though:\n\ndf[df.duplicated(keep=False)].shape[0]\n\n482\n\n\nWe can also specify the columns to use:\n\ndf[df.duplicated(['date', 'station'])].shape[0]\n\n284\n\n\nLet’s look at a few duplicates. Just in the few values we see here, we know that the top 4 are actually in the data 6 times because by default we aren’t seeing their first occurrence:\n\ndf[df.duplicated()].head()\n\n\n\n\n\n\n\n\ndate\nstation\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n1\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2\n2018-01-01T00:00:00\n?\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n5\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n6\n2018-01-03T00:00:00\nGHCND:USC00280907\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n8\n2018-01-04T00:00:00\n?\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue" + }, + { + "objectID": "notebooks/handling_data_issues.html#mitigating-issues", + "href": "notebooks/handling_data_issues.html#mitigating-issues", + "title": "Handling duplicate, missing, or invalid data", + "section": "", + "text": "Since we know we have NY weather data and noticed we only had two entries for station, we may decide to drop the station column because we are only interested in the weather data. However, when dealing with duplicate data, we need to think of the ramifications of removing it. Notice we only have data for the WESF column when the station is ?:\n\ndf[df.WESF.notna()].station.unique()\n\narray(['?'], dtype=object)\n\n\nIf we determine it won’t impact our analysis, we can use drop_duplicates() to remove them:\n\n# 1. make the date a datetime\ndf.date = pd.to_datetime(df.date)\n\n# 2. save this information for later\nstation_qm_wesf = df[df.station == '?'].drop_duplicates('date').set_index('date').WESF\n\n# 3. sort ? to the bottom\ndf.sort_values('station', ascending=False, inplace=True)\n\n# 4. drop duplicates based on the date column keeping the first occurrence \n# which will be the valid station if it has data\ndf_deduped = df.drop_duplicates('date')\n\n# 5. remove the station column because we are done with it\ndf_deduped = df_deduped.drop(columns='station').set_index('date').sort_index()\n\n# 6. take valid station's WESF and fall back on station ? if it is null\ndf_deduped = df_deduped.assign(\n WESF=lambda x: x.WESF.combine_first(station_qm_wesf)\n)\n\ndf_deduped.shape\n\n(324, 8)\n\n\nHere we used the combine_first() method to coalesce the values to the first non-null entry; this means that if we had data from both stations, we would first take the value provided by the named station and if (and only if) that station was null would we take the value from the station named ?. The following table contains some examples of how this would play out:\n\n\n\nstation GHCND:USC00280907\nstation ?\nresult of combine_first()\n\n\n\n\n1\n17\n1\n\n\n1\nNaN\n1\n\n\nNaN\n17\n17\n\n\nNaN\nNaN\nNaN\n\n\n\nCheck out the 4th row—we have WESF in the correct spot thanks to the index:\n\ndf_deduped.head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\nNaN\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\nNaN\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\nNaN\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\nNaN\nTrue\n\n\n\n\n\n\n\n\n\n\nWe could drop nulls, replace them with some arbitrary value, or impute them using the surrounding data. Each of these options may have ramifications, so we must choose wisely.\nWe can use dropna() to drop rows where any column has a null value. The default options leave us hardly any data:\n\ndf_deduped.dropna().shape\n\n(4, 8)\n\n\nIf we pass how='all', we can choose to only drop rows where everything is null, but this removes nothing:\n\ndf_deduped.dropna(how='all').shape\n\n(324, 8)\n\n\nWe can use just a subset of columns to determine what to drop with the subset argument:\n\ndf_deduped.dropna(\n how='all', subset=['inclement_weather', 'SNOW', 'SNWD']\n).shape\n\n(293, 8)\n\n\nThis can also be performed along columns, and we can also require a certain number of null values before we drop the data:\n\ndf_deduped.dropna(axis='columns', thresh=df_deduped.shape[0] * .75).columns\n\nIndex(['PRCP', 'SNOW', 'SNWD', 'TMAX', 'TMIN', 'TOBS', 'inclement_weather'], dtype='object')\n\n\nWe can choose to fill in the null values instead with fillna():\n\ndf_deduped.loc[:,'WESF'].fillna(0, inplace=True)\ndf_deduped.head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\n5505.0\n-40.0\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n5505.0\n-40.0\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nAt this point we have done everything we can without distorting the data. We know that we are missing dates, but if we reindex, we don’t know how to fill in the NaN data. With the weather data, we can’t assume because it snowed one day that it will snow the next or that the temperature will be the same. For this reason, note that the next few examples are just for illustrative purposes only—just because we can do something doesn’t mean we should.\nThat being said, let’s try to address some of remaining issues with the temperature data. We know that when TMAX is the temperature of the Sun, it must be because there was no measured value, so let’s replace it with NaN. We will also do so for TMIN which currently uses -40°C for its placeholder when we know that the coldest temperature ever recorded in NYC was -15°F (-26.1°C) on February 9, 1934:\n\ndf_deduped = df_deduped.assign(\n TMAX=lambda x: x.TMAX.replace(5505, np.nan),\n TMIN=lambda x: x.TMIN.replace(-40, np.nan),\n)\n\nWe will also make an assumption that the temperature won’t change drastically day-to-day. Note that this is actually a big assumption, but it will allow us to understand how fillna() works when we provide a strategy through the method parameter. The fillna() method gives us 2 options for the method parameter: - 'ffill' to forward-fill - 'bfill' to back-fill\nNote that 'nearest' is missing because we are not reindexing.\nHere, we will use 'ffill' to show how this works:\n\ndf_deduped.assign(\n TMAX=lambda x: x.TMAX.fillna(method='ffill'),\n TMIN=lambda x: x.TMIN.fillna(method='ffill')\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n-4.4\n-13.9\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nWe can use np.nan_to_num() to turn np.nan into 0 and -np.inf/np.inf into large negative or positive finite numbers:\n\ndf_deduped.assign(\n SNWD=lambda x: np.nan_to_num(x.SNWD)\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-1.797693e+308\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-1.797693e+308\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-1.797693e+308\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\n1.797693e+308\nNaN\nNaN\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\n1.797693e+308\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nDepending on the data we are working with, we can use the clip() method as an alternative to np.nan_to_num(). The clip() method makes it possible to cap values at a specific minimum and/or maximum threshold. Since SNWD can’t be negative, let’s use clip() to enforce a lower bound of zero. To show how the upper bound works, let’s use the value of SNOW:\n\ndf_deduped.assign(\n SNWD=lambda x: x.SNWD.clip(0, x.SNOW)\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n0.0\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n0.0\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n0.0\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\n229.0\nNaN\nNaN\nNaN\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\n127.0\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nWe can couple fillna() with other types of calculations. Here we replace missing values of TMAX with the median of all TMAX values, TMIN with the median of all TMIN values, and TOBS to the average of the TMAX and TMIN values. Since we place TOBS last, we have access to the imputed values for TMIN and TMAX in the calculation:\n\ndf_deduped.assign(\n TMAX=lambda x: x.TMAX.fillna(x.TMAX.median()),\n TMIN=lambda x: x.TMIN.fillna(x.TMIN.median()),\n # average of TMAX and TMIN\n TOBS=lambda x: x.TOBS.fillna((x.TMAX + x.TMIN) / 2)\n).head()\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\n14.4\n5.6\n10.0\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.1\n-12.2\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.9\n-13.3\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n14.4\n5.6\n10.0\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.9\n-13.9\n0.0\nTrue\n\n\n\n\n\n\n\nWe can also use apply() for running the same calculation across columns. For example, let’s fill all missing values with their rolling 7-day median of their values, setting the number of periods required for the calculation to 0 to ensure we don’t introduce more extra NaN values. Rolling calculations will be covered later on, so this is a preview:\n\ndf_deduped.apply(\n # rolling calculations will be covered later on, this is a rolling 7-day median\n # we set min_periods (# of periods required for calculation) to 0 so we always get a result \n lambda x: x.fillna(x.rolling(7, min_periods=0).median())\n).head(10)\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.30\n-16.1\n-12.20\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.40\n-13.9\n-13.30\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n-6.35\n-15.0\n-12.75\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.40\n-13.9\n-13.90\n0.0\nTrue\n\n\n2018-01-06\n0.0\n0.0\n-inf\n-10.00\n-15.6\n-15.00\n0.0\nFalse\n\n\n2018-01-07\n0.0\n0.0\n-inf\n-11.70\n-17.2\n-16.10\n0.0\nFalse\n\n\n2018-01-08\n0.0\n0.0\n-inf\n-7.80\n-16.7\n-8.30\n0.0\nFalse\n\n\n2018-01-10\n0.0\n0.0\n-inf\n5.00\n-7.8\n-7.80\n0.0\nFalse\n\n\n2018-01-11\n0.0\n0.0\n-inf\n4.40\n-7.8\n1.10\n0.0\nFalse\n\n\n\n\n\n\n\nThe last strategy we could try is interpolation with the interpolate() method. We specify the method parameter with the interpolation strategy to use. There are many options, but we will stick with the default of 'linear', which will treat values as evenly spaced and place missing values in the middle of existing ones. We have some missing data, so we will reindex first. Look at January 9th, which we didn’t have before—the values for TMAX, TMIN, and TOBS are the average of values the day prior (January 8th) and the day after (January 10th):\n\ndf_deduped\\\n .reindex(pd.date_range('2018-01-01', '2018-12-31', freq='D'))\\\n .apply(lambda x: x.interpolate())\\\n .head(10)\n\n\n\n\n\n\n\n\nPRCP\nSNOW\nSNWD\nTMAX\nTMIN\nTOBS\nWESF\ninclement_weather\n\n\n\n\n2018-01-01\n0.0\n0.0\n-inf\nNaN\nNaN\nNaN\n0.0\nNaN\n\n\n2018-01-02\n0.0\n0.0\n-inf\n-8.3\n-16.10\n-12.20\n0.0\nFalse\n\n\n2018-01-03\n0.0\n0.0\n-inf\n-4.4\n-13.90\n-13.30\n0.0\nFalse\n\n\n2018-01-04\n20.6\n229.0\ninf\n-4.4\n-13.90\n-13.60\n19.3\nTrue\n\n\n2018-01-05\n14.2\n127.0\ninf\n-4.4\n-13.90\n-13.90\n0.0\nTrue\n\n\n2018-01-06\n0.0\n0.0\n-inf\n-10.0\n-15.60\n-15.00\n0.0\nFalse\n\n\n2018-01-07\n0.0\n0.0\n-inf\n-11.7\n-17.20\n-16.10\n0.0\nFalse\n\n\n2018-01-08\n0.0\n0.0\n-inf\n-7.8\n-16.70\n-8.30\n0.0\nFalse\n\n\n2018-01-09\n0.0\n0.0\n-inf\n-1.4\n-12.25\n-8.05\n0.0\nNaN\n\n\n2018-01-10\n0.0\n0.0\n-inf\n5.0\n-7.80\n-7.80\n0.0\nFalse" + }, + { + "objectID": "notebooks/cleaning_data.html", + "href": "notebooks/cleaning_data.html", + "title": "Cleaning Data", + "section": "", + "text": "In this notebook, we will using daily temperature data from the National Centers for Environmental Information (NCEI) API. We will use the Global Historical Climatology Network - Daily (GHCND) dataset; see the documentation here.\nThis data was collected from the LaGuardia Airport station in New York City for October 2018. It contains: - the daily minimum temperature (TMIN) - the daily maximum temperature (TMAX) - the daily average temperature (TAVG)\nNote: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.\nIn addition, we will be using S&P 500 stock market data (obtained using the stock_analysis package and data for bitcoin for 2017 through 2018. For the first edition, the bitcoin data was collected from CoinMarketCap using the stock_analysis package; however, changes in the website led to the necessity of changing the data source to Yahoo! Finance. The bitcoin data that was collected before the CoinMarketCap website change should be equivalent to the historical data that can be viewed on this page.\n\n\n\nWe need to import pandas and read in the temperature data to get started:\n\nimport pandas as pd\n\ndf = pd.read_csv('../data/nyc_temperatures.csv')\ndf.head()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nattributes\nvalue\n\n\n\n\n0\n2018-10-01T00:00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n\n\n1\n2018-10-01T00:00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n\n\n2\n2018-10-01T00:00:00\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n\n\n3\n2018-10-02T00:00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n\n\n4\n2018-10-02T00:00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n\n\n\n\n\n\n\n\n\n\nWe start out with the following columns:\n\ndf.columns\n\nIndex(['date', 'datatype', 'station', 'attributes', 'value'], dtype='object')\n\n\nWe want to rename the value column to indicate it contains the temperature in Celsius and the attributes column to say flags since each value in the comma-delimited string is a different flag about the data collection. For this task, we use the rename() method and pass in a dictionary mapping the column names to their new names. We pass inplace=True to change our original dataframe instead of getting a new one back:\n\ndf.rename(\n columns={\n 'value': 'temp_C',\n 'attributes': 'flags'\n }, inplace=True\n)\n\nThose columns have been successfully renamed:\n\ndf.columns\n\nIndex(['date', 'datatype', 'station', 'flags', 'temp_C'], dtype='object')\n\n\nWe can also perform string operations on the column names with rename():\n\ndf.rename(str.upper, axis='columns').columns\n\nIndex(['DATE', 'DATATYPE', 'STATION', 'FLAGS', 'TEMP_C'], dtype='object')\n\n\n\n\n\nThe date column is not currently being stored as a datetime:\n\ndf.dtypes\n\ndate object\ndatatype object\nstation object\nflags object\ntemp_C float64\ndtype: object\n\n\nLet’s perform the conversion with pd.to_datetime():\n\ndf.loc[:,'date'] = pd.to_datetime(df.date)\ndf.dtypes\n\ndate object\ndatatype object\nstation object\nflags object\ntemp_C float64\ndtype: object\n\n\nNow we get useful information when we use describe() on this column:\n\ndf.date.describe()\n\ncount 93\nunique 31\ntop 2018-10-01 00:00:00\nfreq 3\nName: date, dtype: object\n\n\nWe can use tz_localize() on a DatetimeIndex object to convert to a desired timezone:\n\npd.date_range(start='2018-10-25', periods=2, freq='D').tz_localize('EST')\n\nDatetimeIndex(['2018-10-25 00:00:00-05:00', '2018-10-26 00:00:00-05:00'], dtype='datetime64[ns, EST]', freq=None)\n\n\nThis also works with Series/DataFrame objects that have an index of type DatetimeIndex. Let’s read in the CSV again for this example and set the date column to be the index and stored as a datetime:\n\neastern = pd.read_csv(\n '../data/nyc_temperatures.csv', index_col='date', parse_dates=True\n).tz_localize('EST')\neastern.head()\n\n\n\n\n\n\n\n\ndatatype\nstation\nattributes\nvalue\n\n\ndate\n\n\n\n\n\n\n\n\n2018-10-01 00:00:00-05:00\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n\n\n2018-10-01 00:00:00-05:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n\n\n2018-10-01 00:00:00-05:00\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n\n\n2018-10-02 00:00:00-05:00\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n\n\n2018-10-02 00:00:00-05:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n\n\n\n\n\n\n\nWe can use tz_convert() to convert to another timezone from there. If we convert the Eastern datetimes to UTC, they will now be at 5 AM, since pandas will use the offsets to convert:\n\neastern.tz_convert('UTC').head()\n\n\n\n\n\n\n\n\ndatatype\nstation\nattributes\nvalue\n\n\ndate\n\n\n\n\n\n\n\n\n2018-10-01 05:00:00+00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n\n\n2018-10-01 05:00:00+00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n\n\n2018-10-01 05:00:00+00:00\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n\n\n2018-10-02 05:00:00+00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n\n\n2018-10-02 05:00:00+00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n\n\n\n\n\n\n\nWe can change the period of the index as well. We could change the period to be monthly to make it easier to aggregate later.\nThe reason we have to add the parameter within tz_localize() to None for this, is because we’ll get a warning from pandas that our output class PeriodArray doesn’t have time zone information and we’ll lose it.\n\neastern.tz_localize(None).to_period('M').index\n\nPeriodIndex(['2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10'],\n dtype='period[M]', name='date')\n\n\nWe now get a PeriodIndex object, which we can change back into a DatetimeIndex object with to_timestamp():\n\neastern.tz_localize(None).to_period('M').to_timestamp().index\n\nDatetimeIndex(['2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01'],\n dtype='datetime64[ns]', name='date', freq=None)\n\n\nWe can use the assign() method for working with multiple columns at once (or creating new ones). Since our date column has already been converted, we need to read in the data again:\n\ndf = pd.read_csv('../data/nyc_temperatures.csv').rename(\n columns={\n 'value': 'temp_C',\n 'attributes': 'flags'\n }\n)\n\nnew_df = df.assign(\n date=pd.to_datetime(df.date),\n temp_F=(df.temp_C * 9/5) + 32\n)\nnew_df.dtypes\n\ndate datetime64[ns]\ndatatype object\nstation object\nflags object\ntemp_C float64\ntemp_F float64\ndtype: object\n\n\nThe date column now has datetimes and the temp_F column was added:\n\nnew_df.head()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_F\n\n\n\n\n0\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n70.16\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n78.08\n\n\n2\n2018-10-01\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n64.94\n\n\n3\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n72.86\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n78.98\n\n\n\n\n\n\n\nWe can also use astype() to perform conversions. Let’s create columns of the integer portion of the temperatures in Celsius and Fahrenheit. We will use lambda functions (first introduced in Chapter 2, Working with Pandas DataFrames), so that we can use the values being created in the temp_F column to calculate the temp_F_whole column. It is very common (and useful) to use lambda functions with assign():\n\ndf = df.assign(\n date=lambda x: pd.to_datetime(x.date),\n temp_C_whole=lambda x: x.temp_C.astype('int'),\n temp_F=lambda x: (x.temp_C * 9/5) + 32,\n temp_F_whole=lambda x: x.temp_F.astype('int')\n)\n\ndf.head()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n21\n70.16\n70\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n2\n2018-10-01\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n18\n64.94\n64\n\n\n3\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n22\n72.86\n72\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nCreating categories:\n\ndf_with_categories = df.assign(\n station=df.station.astype('category'),\n datatype=df.datatype.astype('category')\n)\ndf_with_categories.dtypes\n\ndate datetime64[ns]\ndatatype category\nstation category\nflags object\ntemp_C float64\ntemp_C_whole int32\ntemp_F float64\ntemp_F_whole int32\ndtype: object\n\n\n\ndf_with_categories.describe(include='category')\n\n\n\n\n\n\n\n\ndatatype\nstation\n\n\n\n\ncount\n93\n93\n\n\nunique\n3\n1\n\n\ntop\nTAVG\nGHCND:USW00014732\n\n\nfreq\n31\n93\n\n\n\n\n\n\n\nOur categories have no order, but this is something that pandas supports:\n\npd.Categorical(\n ['med', 'med', 'low', 'high'], \n categories=['low', 'med', 'high'],\n ordered=True\n)\n\n['med', 'med', 'low', 'high']\nCategories (3, object): ['low' < 'med' < 'high']\n\n\n\n\n\nSay we want to find the days that reached the hottest temperatures in the weather data; we can sort our values by the temp_C column with the largest on top to find this:\n\ndf[df.datatype == 'TMAX'].sort_values(by='temp_C', ascending=False).head(10)\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n19\n2018-10-07\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n28\n2018-10-10\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n31\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n10\n2018-10-04\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n25\n2018-10-09\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n7\n2018-10-03\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.0\n25\n77.00\n77\n\n\n13\n2018-10-05\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n22\n2018-10-08\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n\n\n\n\n\nHowever, this isn’t perfect because we have some ties, and they aren’t sorted consistently. In the first tie between the 7th and the 10th, the earlier date comes first, but the opposite is true with the tie between the 4th and the 2nd. We can use other columns to break ties and specify how to sort each with ascending. Let’s break ties with the date column and show earlier dates before later ones:\n\ndf[df.datatype == 'TMAX'].sort_values(by=['temp_C', 'date'], ascending=[False, True]).head(10)\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n19\n2018-10-07\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n28\n2018-10-10\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n31\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n10\n2018-10-04\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n25\n2018-10-09\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n7\n2018-10-03\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.0\n25\n77.00\n77\n\n\n13\n2018-10-05\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n22\n2018-10-08\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n\n\n\n\n\nNotice that the index was jumbled in the past 2 results. Here, our index only stores the row number in the original data, but we may not need to keep track of that information. In this case, we can pass in ignore_index=True to get a new index after sorting:\n\ndf[df.datatype == 'TMAX'].sort_values(by=['temp_C', 'date'], ascending=[False, True], ignore_index=True).head(10)\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n2018-10-07\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n1\n2018-10-10\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n2\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n3\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n4\n2018-10-04\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n5\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n6\n2018-10-09\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n7\n2018-10-03\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.0\n25\n77.00\n77\n\n\n8\n2018-10-05\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n9\n2018-10-08\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n\n\n\n\n\nWhen just looking for the n-largest values, rather than wanting to sort all the data, we can use nlargest():\n\ndf[df.datatype == 'TAVG'].nlargest(n=10, columns='temp_C')\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n27\n2018-10-10\nTAVG\nGHCND:USW00014732\nH,,S,\n23.8\n23\n74.84\n74\n\n\n30\n2018-10-11\nTAVG\nGHCND:USW00014732\nH,,S,\n23.4\n23\n74.12\n74\n\n\n18\n2018-10-07\nTAVG\nGHCND:USW00014732\nH,,S,\n22.8\n22\n73.04\n73\n\n\n3\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n22\n72.86\n72\n\n\n6\n2018-10-03\nTAVG\nGHCND:USW00014732\nH,,S,\n21.8\n21\n71.24\n71\n\n\n24\n2018-10-09\nTAVG\nGHCND:USW00014732\nH,,S,\n21.8\n21\n71.24\n71\n\n\n9\n2018-10-04\nTAVG\nGHCND:USW00014732\nH,,S,\n21.3\n21\n70.34\n70\n\n\n0\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n21\n70.16\n70\n\n\n21\n2018-10-08\nTAVG\nGHCND:USW00014732\nH,,S,\n20.9\n20\n69.62\n69\n\n\n12\n2018-10-05\nTAVG\nGHCND:USW00014732\nH,,S,\n20.3\n20\n68.54\n68\n\n\n\n\n\n\n\nWe use nsmallest() for the n-smallest values.\n\ndf.nsmallest(n=5, columns=['temp_C', 'date'])\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n65\n2018-10-22\nTMIN\nGHCND:USW00014732\n,,W,2400\n5.6\n5\n42.08\n42\n\n\n77\n2018-10-26\nTMIN\nGHCND:USW00014732\n,,W,2400\n5.6\n5\n42.08\n42\n\n\n62\n2018-10-21\nTMIN\nGHCND:USW00014732\n,,W,2400\n6.1\n6\n42.98\n42\n\n\n74\n2018-10-25\nTMIN\nGHCND:USW00014732\n,,W,2400\n6.1\n6\n42.98\n42\n\n\n53\n2018-10-18\nTMIN\nGHCND:USW00014732\n,,W,2400\n6.7\n6\n44.06\n44\n\n\n\n\n\n\n\nThe sample() method will give us rows (or columns with axis=1) at random. We can provide a seed (random_state) to make this reproducible. The index after we do this is jumbled:\n\ndf.sample(5, random_state=0).index\n\nIndex([2, 30, 55, 16, 13], dtype='int64')\n\n\nWe can use sort_index() to order it again:\n\ndf.sample(5, random_state=0).sort_index().index\n\nIndex([2, 13, 16, 30, 55], dtype='int64')\n\n\nThe sort_index() method can also sort columns alphabetically:\n\ndf.sort_index(axis=1).head()\n\n\n\n\n\n\n\n\ndatatype\ndate\nflags\nstation\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\nTAVG\n2018-10-01\nH,,S,\nGHCND:USW00014732\n21.2\n21\n70.16\n70\n\n\n1\nTMAX\n2018-10-01\n,,W,2400\nGHCND:USW00014732\n25.6\n25\n78.08\n78\n\n\n2\nTMIN\n2018-10-01\n,,W,2400\nGHCND:USW00014732\n18.3\n18\n64.94\n64\n\n\n3\nTAVG\n2018-10-02\nH,,S,\nGHCND:USW00014732\n22.7\n22\n72.86\n72\n\n\n4\nTMAX\n2018-10-02\n,,W,2400\nGHCND:USW00014732\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nThis can make selection with loc easier for many columns:\n\ndf.sort_index(axis=1).head().loc[:,'temp_C':'temp_F_whole']\n\n\n\n\n\n\n\n\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n21.2\n21\n70.16\n70\n\n\n1\n25.6\n25\n78.08\n78\n\n\n2\n18.3\n18\n64.94\n64\n\n\n3\n22.7\n22\n72.86\n72\n\n\n4\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nWe must sort the index to compare two dataframes. If the index is different, but the data is the same, they will be marked not-equal:\n\ndf.equals(df.sort_values(by='temp_C'))\n\nFalse\n\n\nSorting the index solves this issue:\n\ndf.equals(df.sort_values(by='temp_C').sort_index())\n\nTrue\n\n\nLet’s set the date column as our index:\n\ndf.set_index('date', inplace=True)\ndf.head()\n\n\n\n\n\n\n\n\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n21\n70.16\n70\n\n\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n2018-10-01\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n18\n64.94\n64\n\n\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n22\n72.86\n72\n\n\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nNow that we have an index of type DatetimeIndex, we can do datetime slicing and indexing. As long as we provide a date format that pandas understands, we can grab the data. To select all of 2018, we simply use df.loc['2018'], for the fourth quarter of 2018 we can use df.loc['2018-Q4'], grabbing October is as simple as using df.loc['2018-10']; these can also be combined to build ranges. Let’s grab October 11, 2018 through October 12, 2018 (inclusive of both endpoints)—note that using loc[] is optional for ranges:\n\ndf['2018-10-11':'2018-10-12']\n\n\n\n\n\n\n\n\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n2018-10-11\nTAVG\nGHCND:USW00014732\nH,,S,\n23.4\n23\n74.12\n74\n\n\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n2018-10-11\nTMIN\nGHCND:USW00014732\n,,W,2400\n21.7\n21\n71.06\n71\n\n\n2018-10-12\nTAVG\nGHCND:USW00014732\nH,,S,\n18.3\n18\n64.94\n64\n\n\n2018-10-12\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.2\n22\n71.96\n71\n\n\n2018-10-12\nTMIN\nGHCND:USW00014732\n,,W,2400\n12.2\n12\n53.96\n53\n\n\n\n\n\n\n\nWe can also use reset_index() to get a fresh index and move our current index into a column for safe keeping. This is especially useful if we had data, such as the date, in the index that we don’t want to lose:\n\ndf['2018-10-11':'2018-10-12'].reset_index()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n2018-10-11\nTAVG\nGHCND:USW00014732\nH,,S,\n23.4\n23\n74.12\n74\n\n\n1\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n2\n2018-10-11\nTMIN\nGHCND:USW00014732\n,,W,2400\n21.7\n21\n71.06\n71\n\n\n3\n2018-10-12\nTAVG\nGHCND:USW00014732\nH,,S,\n18.3\n18\n64.94\n64\n\n\n4\n2018-10-12\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.2\n22\n71.96\n71\n\n\n5\n2018-10-12\nTMIN\nGHCND:USW00014732\n,,W,2400\n12.2\n12\n53.96\n53\n\n\n\n\n\n\n\nReindexing allows us to conform our axis to contain a given set of labels. Let’s turn to the S&P 500 stock data in the sp500.csv file to see an example of this. Notice we only have data for trading days (weekdays, excluding holidays):\n\nsp = pd.read_csv(\n '../data/sp500.csv', index_col='date', parse_dates=True\n).drop(columns=['adj_close'])\n\nsp.head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3770530000\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3764890000\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3761820000\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3339890000\nFriday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3217610000\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3638790000\nTuesday\n\n\n2017-01-11\n2275.320068\n2260.830078\n2268.600098\n2275.320068\n3620410000\nWednesday\n\n\n2017-01-12\n2271.780029\n2254.250000\n2271.139893\n2270.439941\n3462130000\nThursday\n\n\n2017-01-13\n2278.679932\n2271.510010\n2272.739990\n2274.639893\n3081270000\nFriday\n\n\n2017-01-17\n2272.080078\n2262.810059\n2269.139893\n2267.889893\n3584990000\nTuesday\n\n\n\n\n\n\n\nIf we want to look at the value of a portfolio (group of assets) that trade on different days, we need to handle the mismatch in the index. Bitcoin, for example, trades daily. If we sum up all the data we have for each day (aggregations will be covered in chapter 4, so don’t fixate on this part), we get the following:\n\nbitcoin = pd.read_csv(\n '../data/bitcoin.csv', index_col='date', parse_dates=True\n).drop(columns=['market_cap'])\n\n# every day's closing price = S&P 500 close + Bitcoin close (same for other metrics)\nportfolio = pd.concat([sp, bitcoin], sort=False).groupby(level='date').sum()\n\nportfolio.head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\n1003.080000\n958.700000\n963.660000\n998.330000\n147775008\nSunday\n\n\n2017-01-02\n1031.390000\n996.700000\n998.620000\n1021.750000\n222184992\nMonday\n\n\n2017-01-03\n3307.959883\n3266.729883\n3273.170068\n3301.670078\n3955698000\nTuesday\n\n\n2017-01-04\n3432.240068\n3306.000098\n3306.000098\n3425.480000\n4109835984\nWednesday\n\n\n2017-01-05\n3462.600000\n3170.869951\n3424.909932\n3282.380000\n4272019008\nThursday\n\n\n2017-01-06\n3328.910098\n3148.000059\n3285.379893\n3179.179980\n3691766000\nFriday\n\n\n2017-01-07\n908.590000\n823.560000\n903.490000\n908.590000\n279550016\nSaturday\n\n\n2017-01-08\n942.720000\n887.250000\n908.170000\n911.200000\n158715008\nSunday\n\n\n2017-01-09\n3189.179990\n3148.709902\n3186.830088\n3171.729902\n3359486992\nMonday\n\n\n2017-01-10\n3194.140020\n3166.330020\n3172.159971\n3176.579902\n3754598000\nTuesday\n\n\n\n\n\n\n\nIt may not be immediately obvious what is wrong with the previous data, but with a visualization we can easily see the cyclical pattern of drops on the days the stock market is closed. (Don’t worry about the plotting code too much, we will cover it in depth in chapters 5 and 6).\nWe will need to import matplotlib now:\n\nimport matplotlib.pyplot as plt # we use this module for plotting\nfrom matplotlib.ticker import StrMethodFormatter # for formatting the axis\n\nNow we can see why we need to reindex:\n\n# plot the closing price from Q4 2017 through Q2 2018\nax = portfolio['2017-Q4':'2018-Q2'].plot(\n y='close', figsize=(15, 5), legend=False,\n title='Bitcoin + S&P 500 value without accounting for different indices'\n)\n\n# formatting\nax.set_ylabel('price')\nax.yaxis.set_major_formatter(StrMethodFormatter('${x:,.0f}'))\nfor spine in ['top', 'right']:\n ax.spines[spine].set_visible(False)\n\n# show the plot\nplt.show()\n\n\n\n\n\n\n\n\nWe need to align the index of the S&P 500 to match bitcoin in order to fix this. We will use the reindex() method, but by default we get NaN for the values that we don’t have data for:\n\nsp.reindex(bitcoin.index).head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\nNaN\nNaN\nNaN\nNaN\nNaN\nSunday\n\n\n2017-01-02\nNaN\nNaN\nNaN\nNaN\nNaN\nMonday\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3.770530e+09\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3.764890e+09\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3.761820e+09\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nFriday\n\n\n2017-01-07\nNaN\nNaN\nNaN\nNaN\nNaN\nSaturday\n\n\n2017-01-08\nNaN\nNaN\nNaN\nNaN\nNaN\nSunday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3.217610e+09\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3.638790e+09\nTuesday\n\n\n\n\n\n\n\nSo now we have rows for every day of the year, but all the weekends and holidays have NaN values. To address this, we can specify how to handle missing values with the method argument. In this case, we want to forward-fill, which will put the weekend and holiday values as the value they had for the Friday (or end of trading week) before:\n\nsp.reindex(bitcoin.index, method='ffill').head(10)\\\n .assign(day_of_week=lambda x: x.index.day_name())\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\nNaN\nNaN\nNaN\nNaN\nNaN\nSunday\n\n\n2017-01-02\nNaN\nNaN\nNaN\nNaN\nNaN\nMonday\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3.770530e+09\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3.764890e+09\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3.761820e+09\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nFriday\n\n\n2017-01-07\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nSaturday\n\n\n2017-01-08\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nSunday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3.217610e+09\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3.638790e+09\nTuesday\n\n\n\n\n\n\n\nTo isolate the changes happening with the forward-filling, we can use the compare() method. It shows us the values that differ across identically-labeled dataframes (same names and same columns). Here, we can see that only weekends and holidays (Monday, January 16, 2017 was MLK day) have values forward-filled. Notice that consecutive days have the same values.\n\nsp.reindex(bitcoin.index)\\\n .compare(sp.reindex(bitcoin.index, method='ffill'))\\\n .head(10).assign(day_of_week=lambda x: x.index.day_name())\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\n\nself\nother\nself\nother\nself\nother\nself\nother\nself\nother\n\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n2017-01-07\nNaN\n2282.100098\nNaN\n2264.060059\nNaN\n2271.139893\nNaN\n2276.979980\nNaN\n3.339890e+09\nSaturday\n\n\n2017-01-08\nNaN\n2282.100098\nNaN\n2264.060059\nNaN\n2271.139893\nNaN\n2276.979980\nNaN\n3.339890e+09\nSunday\n\n\n2017-01-14\nNaN\n2278.679932\nNaN\n2271.510010\nNaN\n2272.739990\nNaN\n2274.639893\nNaN\n3.081270e+09\nSaturday\n\n\n2017-01-15\nNaN\n2278.679932\nNaN\n2271.510010\nNaN\n2272.739990\nNaN\n2274.639893\nNaN\n3.081270e+09\nSunday\n\n\n2017-01-16\nNaN\n2278.679932\nNaN\n2271.510010\nNaN\n2272.739990\nNaN\n2274.639893\nNaN\n3.081270e+09\nMonday\n\n\n2017-01-21\nNaN\n2276.959961\nNaN\n2265.010010\nNaN\n2269.959961\nNaN\n2271.310059\nNaN\n3.524970e+09\nSaturday\n\n\n2017-01-22\nNaN\n2276.959961\nNaN\n2265.010010\nNaN\n2269.959961\nNaN\n2271.310059\nNaN\n3.524970e+09\nSunday\n\n\n2017-01-28\nNaN\n2299.020020\nNaN\n2291.620117\nNaN\n2299.020020\nNaN\n2294.689941\nNaN\n3.135890e+09\nSaturday\n\n\n2017-01-29\nNaN\n2299.020020\nNaN\n2291.620117\nNaN\n2299.020020\nNaN\n2294.689941\nNaN\n3.135890e+09\nSunday\n\n\n2017-02-04\nNaN\n2298.310059\nNaN\n2287.879883\nNaN\n2288.540039\nNaN\n2297.419922\nNaN\n3.597970e+09\nSaturday\n\n\n\n\n\n\n\nThis isn’t perfect though. We probably want 0 for the volume traded and to put the closing price for the open, high, low, and close on the days the market is closed:\nThe reason why we’re using np.where(boolean condition, value if True, value if False) within lambda functions in the example below, is that vectorized operations allow us to be faster and more efficient than utilizing for loops to perform calculations on arrays all at once.\n\nimport numpy as np\n\nsp_reindexed = sp.reindex(bitcoin.index).assign(\n volume=lambda x: x.volume.fillna(0), # put 0 when market is closed\n close=lambda x: x.close.fillna(method='ffill'), # carry this forward\n # take the closing price if these aren't available\n open=lambda x: np.where(x.open.isnull(), x.close, x.open),\n high=lambda x: np.where(x.high.isnull(), x.close, x.high),\n low=lambda x: np.where(x.low.isnull(), x.close, x.low)\n)\nsp_reindexed.head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\nNaN\nNaN\nNaN\nNaN\n0.000000e+00\nSunday\n\n\n2017-01-02\nNaN\nNaN\nNaN\nNaN\n0.000000e+00\nMonday\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3.770530e+09\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3.764890e+09\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3.761820e+09\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nFriday\n\n\n2017-01-07\n2276.979980\n2276.979980\n2276.979980\n2276.979980\n0.000000e+00\nSaturday\n\n\n2017-01-08\n2276.979980\n2276.979980\n2276.979980\n2276.979980\n0.000000e+00\nSunday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3.217610e+09\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3.638790e+09\nTuesday\n\n\n\n\n\n\n\nIf we create a visualization comparing the reindexed data to the first attempt, we see how reindexing helped maintain the asset value when the market was closed:\n\n# every day's closing price = S&P 500 close adjusted for market closure + Bitcoin close (same for other metrics)\nfixed_portfolio = sp_reindexed + bitcoin\n\n# plot the reindexed portfolio's closing price from Q4 2017 through Q2 2018\nax = fixed_portfolio['2017-Q4':'2018-Q2'].plot(\n y='close', label='reindexed portfolio of S&P 500 + Bitcoin', figsize=(15, 5), linewidth=2, \n title='Reindexed portfolio vs. portfolio with mismatched indices'\n)\n\n# add line for original portfolio for comparison\nportfolio['2017-Q4':'2018-Q2'].plot(\n y='close', ax=ax, linestyle='--', label='portfolio of S&P 500 + Bitcoin w/o reindexing'\n)\n\n# formatting\nax.set_ylabel('price')\nax.yaxis.set_major_formatter(StrMethodFormatter('${x:,.0f}'))\nfor spine in ['top', 'right']:\n ax.spines[spine].set_visible(False)\n\n# show the plot\nplt.show()" + }, + { + "objectID": "notebooks/cleaning_data.html#about-the-data", + "href": "notebooks/cleaning_data.html#about-the-data", + "title": "Cleaning Data", + "section": "", + "text": "In this notebook, we will using daily temperature data from the National Centers for Environmental Information (NCEI) API. We will use the Global Historical Climatology Network - Daily (GHCND) dataset; see the documentation here.\nThis data was collected from the LaGuardia Airport station in New York City for October 2018. It contains: - the daily minimum temperature (TMIN) - the daily maximum temperature (TMAX) - the daily average temperature (TAVG)\nNote: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) and, as you can see from the URL for the API, this resource was created when the NCEI was called the NCDC. Should the URL for this resource change in the future, you can search for “NCEI weather API” to find the updated one.\nIn addition, we will be using S&P 500 stock market data (obtained using the stock_analysis package and data for bitcoin for 2017 through 2018. For the first edition, the bitcoin data was collected from CoinMarketCap using the stock_analysis package; however, changes in the website led to the necessity of changing the data source to Yahoo! Finance. The bitcoin data that was collected before the CoinMarketCap website change should be equivalent to the historical data that can be viewed on this page." + }, + { + "objectID": "notebooks/cleaning_data.html#setup", + "href": "notebooks/cleaning_data.html#setup", + "title": "Cleaning Data", + "section": "", + "text": "We need to import pandas and read in the temperature data to get started:\n\nimport pandas as pd\n\ndf = pd.read_csv('../data/nyc_temperatures.csv')\ndf.head()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nattributes\nvalue\n\n\n\n\n0\n2018-10-01T00:00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n\n\n1\n2018-10-01T00:00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n\n\n2\n2018-10-01T00:00:00\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n\n\n3\n2018-10-02T00:00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n\n\n4\n2018-10-02T00:00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1" + }, + { + "objectID": "notebooks/cleaning_data.html#renaming-columns", + "href": "notebooks/cleaning_data.html#renaming-columns", + "title": "Cleaning Data", + "section": "", + "text": "We start out with the following columns:\n\ndf.columns\n\nIndex(['date', 'datatype', 'station', 'attributes', 'value'], dtype='object')\n\n\nWe want to rename the value column to indicate it contains the temperature in Celsius and the attributes column to say flags since each value in the comma-delimited string is a different flag about the data collection. For this task, we use the rename() method and pass in a dictionary mapping the column names to their new names. We pass inplace=True to change our original dataframe instead of getting a new one back:\n\ndf.rename(\n columns={\n 'value': 'temp_C',\n 'attributes': 'flags'\n }, inplace=True\n)\n\nThose columns have been successfully renamed:\n\ndf.columns\n\nIndex(['date', 'datatype', 'station', 'flags', 'temp_C'], dtype='object')\n\n\nWe can also perform string operations on the column names with rename():\n\ndf.rename(str.upper, axis='columns').columns\n\nIndex(['DATE', 'DATATYPE', 'STATION', 'FLAGS', 'TEMP_C'], dtype='object')" + }, + { + "objectID": "notebooks/cleaning_data.html#type-conversion", + "href": "notebooks/cleaning_data.html#type-conversion", + "title": "Cleaning Data", + "section": "", + "text": "The date column is not currently being stored as a datetime:\n\ndf.dtypes\n\ndate object\ndatatype object\nstation object\nflags object\ntemp_C float64\ndtype: object\n\n\nLet’s perform the conversion with pd.to_datetime():\n\ndf.loc[:,'date'] = pd.to_datetime(df.date)\ndf.dtypes\n\ndate object\ndatatype object\nstation object\nflags object\ntemp_C float64\ndtype: object\n\n\nNow we get useful information when we use describe() on this column:\n\ndf.date.describe()\n\ncount 93\nunique 31\ntop 2018-10-01 00:00:00\nfreq 3\nName: date, dtype: object\n\n\nWe can use tz_localize() on a DatetimeIndex object to convert to a desired timezone:\n\npd.date_range(start='2018-10-25', periods=2, freq='D').tz_localize('EST')\n\nDatetimeIndex(['2018-10-25 00:00:00-05:00', '2018-10-26 00:00:00-05:00'], dtype='datetime64[ns, EST]', freq=None)\n\n\nThis also works with Series/DataFrame objects that have an index of type DatetimeIndex. Let’s read in the CSV again for this example and set the date column to be the index and stored as a datetime:\n\neastern = pd.read_csv(\n '../data/nyc_temperatures.csv', index_col='date', parse_dates=True\n).tz_localize('EST')\neastern.head()\n\n\n\n\n\n\n\n\ndatatype\nstation\nattributes\nvalue\n\n\ndate\n\n\n\n\n\n\n\n\n2018-10-01 00:00:00-05:00\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n\n\n2018-10-01 00:00:00-05:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n\n\n2018-10-01 00:00:00-05:00\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n\n\n2018-10-02 00:00:00-05:00\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n\n\n2018-10-02 00:00:00-05:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n\n\n\n\n\n\n\nWe can use tz_convert() to convert to another timezone from there. If we convert the Eastern datetimes to UTC, they will now be at 5 AM, since pandas will use the offsets to convert:\n\neastern.tz_convert('UTC').head()\n\n\n\n\n\n\n\n\ndatatype\nstation\nattributes\nvalue\n\n\ndate\n\n\n\n\n\n\n\n\n2018-10-01 05:00:00+00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n\n\n2018-10-01 05:00:00+00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n\n\n2018-10-01 05:00:00+00:00\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n\n\n2018-10-02 05:00:00+00:00\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n\n\n2018-10-02 05:00:00+00:00\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n\n\n\n\n\n\n\nWe can change the period of the index as well. We could change the period to be monthly to make it easier to aggregate later.\nThe reason we have to add the parameter within tz_localize() to None for this, is because we’ll get a warning from pandas that our output class PeriodArray doesn’t have time zone information and we’ll lose it.\n\neastern.tz_localize(None).to_period('M').index\n\nPeriodIndex(['2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10', '2018-10', '2018-10', '2018-10',\n '2018-10', '2018-10', '2018-10'],\n dtype='period[M]', name='date')\n\n\nWe now get a PeriodIndex object, which we can change back into a DatetimeIndex object with to_timestamp():\n\neastern.tz_localize(None).to_period('M').to_timestamp().index\n\nDatetimeIndex(['2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01',\n '2018-10-01'],\n dtype='datetime64[ns]', name='date', freq=None)\n\n\nWe can use the assign() method for working with multiple columns at once (or creating new ones). Since our date column has already been converted, we need to read in the data again:\n\ndf = pd.read_csv('../data/nyc_temperatures.csv').rename(\n columns={\n 'value': 'temp_C',\n 'attributes': 'flags'\n }\n)\n\nnew_df = df.assign(\n date=pd.to_datetime(df.date),\n temp_F=(df.temp_C * 9/5) + 32\n)\nnew_df.dtypes\n\ndate datetime64[ns]\ndatatype object\nstation object\nflags object\ntemp_C float64\ntemp_F float64\ndtype: object\n\n\nThe date column now has datetimes and the temp_F column was added:\n\nnew_df.head()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_F\n\n\n\n\n0\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n70.16\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n78.08\n\n\n2\n2018-10-01\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n64.94\n\n\n3\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n72.86\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n78.98\n\n\n\n\n\n\n\nWe can also use astype() to perform conversions. Let’s create columns of the integer portion of the temperatures in Celsius and Fahrenheit. We will use lambda functions (first introduced in Chapter 2, Working with Pandas DataFrames), so that we can use the values being created in the temp_F column to calculate the temp_F_whole column. It is very common (and useful) to use lambda functions with assign():\n\ndf = df.assign(\n date=lambda x: pd.to_datetime(x.date),\n temp_C_whole=lambda x: x.temp_C.astype('int'),\n temp_F=lambda x: (x.temp_C * 9/5) + 32,\n temp_F_whole=lambda x: x.temp_F.astype('int')\n)\n\ndf.head()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n21\n70.16\n70\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n2\n2018-10-01\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n18\n64.94\n64\n\n\n3\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n22\n72.86\n72\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nCreating categories:\n\ndf_with_categories = df.assign(\n station=df.station.astype('category'),\n datatype=df.datatype.astype('category')\n)\ndf_with_categories.dtypes\n\ndate datetime64[ns]\ndatatype category\nstation category\nflags object\ntemp_C float64\ntemp_C_whole int32\ntemp_F float64\ntemp_F_whole int32\ndtype: object\n\n\n\ndf_with_categories.describe(include='category')\n\n\n\n\n\n\n\n\ndatatype\nstation\n\n\n\n\ncount\n93\n93\n\n\nunique\n3\n1\n\n\ntop\nTAVG\nGHCND:USW00014732\n\n\nfreq\n31\n93\n\n\n\n\n\n\n\nOur categories have no order, but this is something that pandas supports:\n\npd.Categorical(\n ['med', 'med', 'low', 'high'], \n categories=['low', 'med', 'high'],\n ordered=True\n)\n\n['med', 'med', 'low', 'high']\nCategories (3, object): ['low' < 'med' < 'high']" + }, + { + "objectID": "notebooks/cleaning_data.html#reordering-reindexing-and-sorting", + "href": "notebooks/cleaning_data.html#reordering-reindexing-and-sorting", + "title": "Cleaning Data", + "section": "", + "text": "Say we want to find the days that reached the hottest temperatures in the weather data; we can sort our values by the temp_C column with the largest on top to find this:\n\ndf[df.datatype == 'TMAX'].sort_values(by='temp_C', ascending=False).head(10)\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n19\n2018-10-07\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n28\n2018-10-10\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n31\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n10\n2018-10-04\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n25\n2018-10-09\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n7\n2018-10-03\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.0\n25\n77.00\n77\n\n\n13\n2018-10-05\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n22\n2018-10-08\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n\n\n\n\n\nHowever, this isn’t perfect because we have some ties, and they aren’t sorted consistently. In the first tie between the 7th and the 10th, the earlier date comes first, but the opposite is true with the tie between the 4th and the 2nd. We can use other columns to break ties and specify how to sort each with ascending. Let’s break ties with the date column and show earlier dates before later ones:\n\ndf[df.datatype == 'TMAX'].sort_values(by=['temp_C', 'date'], ascending=[False, True]).head(10)\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n19\n2018-10-07\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n28\n2018-10-10\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n31\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n4\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n10\n2018-10-04\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n1\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n25\n2018-10-09\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n7\n2018-10-03\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.0\n25\n77.00\n77\n\n\n13\n2018-10-05\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n22\n2018-10-08\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n\n\n\n\n\nNotice that the index was jumbled in the past 2 results. Here, our index only stores the row number in the original data, but we may not need to keep track of that information. In this case, we can pass in ignore_index=True to get a new index after sorting:\n\ndf[df.datatype == 'TMAX'].sort_values(by=['temp_C', 'date'], ascending=[False, True], ignore_index=True).head(10)\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n2018-10-07\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n1\n2018-10-10\nTMAX\nGHCND:USW00014732\n,,W,2400\n27.8\n27\n82.04\n82\n\n\n2\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n3\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n4\n2018-10-04\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n5\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n6\n2018-10-09\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n7\n2018-10-03\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.0\n25\n77.00\n77\n\n\n8\n2018-10-05\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n9\n2018-10-08\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.8\n22\n73.04\n73\n\n\n\n\n\n\n\nWhen just looking for the n-largest values, rather than wanting to sort all the data, we can use nlargest():\n\ndf[df.datatype == 'TAVG'].nlargest(n=10, columns='temp_C')\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n27\n2018-10-10\nTAVG\nGHCND:USW00014732\nH,,S,\n23.8\n23\n74.84\n74\n\n\n30\n2018-10-11\nTAVG\nGHCND:USW00014732\nH,,S,\n23.4\n23\n74.12\n74\n\n\n18\n2018-10-07\nTAVG\nGHCND:USW00014732\nH,,S,\n22.8\n22\n73.04\n73\n\n\n3\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n22\n72.86\n72\n\n\n6\n2018-10-03\nTAVG\nGHCND:USW00014732\nH,,S,\n21.8\n21\n71.24\n71\n\n\n24\n2018-10-09\nTAVG\nGHCND:USW00014732\nH,,S,\n21.8\n21\n71.24\n71\n\n\n9\n2018-10-04\nTAVG\nGHCND:USW00014732\nH,,S,\n21.3\n21\n70.34\n70\n\n\n0\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n21\n70.16\n70\n\n\n21\n2018-10-08\nTAVG\nGHCND:USW00014732\nH,,S,\n20.9\n20\n69.62\n69\n\n\n12\n2018-10-05\nTAVG\nGHCND:USW00014732\nH,,S,\n20.3\n20\n68.54\n68\n\n\n\n\n\n\n\nWe use nsmallest() for the n-smallest values.\n\ndf.nsmallest(n=5, columns=['temp_C', 'date'])\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n65\n2018-10-22\nTMIN\nGHCND:USW00014732\n,,W,2400\n5.6\n5\n42.08\n42\n\n\n77\n2018-10-26\nTMIN\nGHCND:USW00014732\n,,W,2400\n5.6\n5\n42.08\n42\n\n\n62\n2018-10-21\nTMIN\nGHCND:USW00014732\n,,W,2400\n6.1\n6\n42.98\n42\n\n\n74\n2018-10-25\nTMIN\nGHCND:USW00014732\n,,W,2400\n6.1\n6\n42.98\n42\n\n\n53\n2018-10-18\nTMIN\nGHCND:USW00014732\n,,W,2400\n6.7\n6\n44.06\n44\n\n\n\n\n\n\n\nThe sample() method will give us rows (or columns with axis=1) at random. We can provide a seed (random_state) to make this reproducible. The index after we do this is jumbled:\n\ndf.sample(5, random_state=0).index\n\nIndex([2, 30, 55, 16, 13], dtype='int64')\n\n\nWe can use sort_index() to order it again:\n\ndf.sample(5, random_state=0).sort_index().index\n\nIndex([2, 13, 16, 30, 55], dtype='int64')\n\n\nThe sort_index() method can also sort columns alphabetically:\n\ndf.sort_index(axis=1).head()\n\n\n\n\n\n\n\n\ndatatype\ndate\nflags\nstation\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\nTAVG\n2018-10-01\nH,,S,\nGHCND:USW00014732\n21.2\n21\n70.16\n70\n\n\n1\nTMAX\n2018-10-01\n,,W,2400\nGHCND:USW00014732\n25.6\n25\n78.08\n78\n\n\n2\nTMIN\n2018-10-01\n,,W,2400\nGHCND:USW00014732\n18.3\n18\n64.94\n64\n\n\n3\nTAVG\n2018-10-02\nH,,S,\nGHCND:USW00014732\n22.7\n22\n72.86\n72\n\n\n4\nTMAX\n2018-10-02\n,,W,2400\nGHCND:USW00014732\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nThis can make selection with loc easier for many columns:\n\ndf.sort_index(axis=1).head().loc[:,'temp_C':'temp_F_whole']\n\n\n\n\n\n\n\n\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n21.2\n21\n70.16\n70\n\n\n1\n25.6\n25\n78.08\n78\n\n\n2\n18.3\n18\n64.94\n64\n\n\n3\n22.7\n22\n72.86\n72\n\n\n4\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nWe must sort the index to compare two dataframes. If the index is different, but the data is the same, they will be marked not-equal:\n\ndf.equals(df.sort_values(by='temp_C'))\n\nFalse\n\n\nSorting the index solves this issue:\n\ndf.equals(df.sort_values(by='temp_C').sort_index())\n\nTrue\n\n\nLet’s set the date column as our index:\n\ndf.set_index('date', inplace=True)\ndf.head()\n\n\n\n\n\n\n\n\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n2018-10-01\nTAVG\nGHCND:USW00014732\nH,,S,\n21.2\n21\n70.16\n70\n\n\n2018-10-01\nTMAX\nGHCND:USW00014732\n,,W,2400\n25.6\n25\n78.08\n78\n\n\n2018-10-01\nTMIN\nGHCND:USW00014732\n,,W,2400\n18.3\n18\n64.94\n64\n\n\n2018-10-02\nTAVG\nGHCND:USW00014732\nH,,S,\n22.7\n22\n72.86\n72\n\n\n2018-10-02\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.1\n26\n78.98\n78\n\n\n\n\n\n\n\nNow that we have an index of type DatetimeIndex, we can do datetime slicing and indexing. As long as we provide a date format that pandas understands, we can grab the data. To select all of 2018, we simply use df.loc['2018'], for the fourth quarter of 2018 we can use df.loc['2018-Q4'], grabbing October is as simple as using df.loc['2018-10']; these can also be combined to build ranges. Let’s grab October 11, 2018 through October 12, 2018 (inclusive of both endpoints)—note that using loc[] is optional for ranges:\n\ndf['2018-10-11':'2018-10-12']\n\n\n\n\n\n\n\n\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n2018-10-11\nTAVG\nGHCND:USW00014732\nH,,S,\n23.4\n23\n74.12\n74\n\n\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n2018-10-11\nTMIN\nGHCND:USW00014732\n,,W,2400\n21.7\n21\n71.06\n71\n\n\n2018-10-12\nTAVG\nGHCND:USW00014732\nH,,S,\n18.3\n18\n64.94\n64\n\n\n2018-10-12\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.2\n22\n71.96\n71\n\n\n2018-10-12\nTMIN\nGHCND:USW00014732\n,,W,2400\n12.2\n12\n53.96\n53\n\n\n\n\n\n\n\nWe can also use reset_index() to get a fresh index and move our current index into a column for safe keeping. This is especially useful if we had data, such as the date, in the index that we don’t want to lose:\n\ndf['2018-10-11':'2018-10-12'].reset_index()\n\n\n\n\n\n\n\n\ndate\ndatatype\nstation\nflags\ntemp_C\ntemp_C_whole\ntemp_F\ntemp_F_whole\n\n\n\n\n0\n2018-10-11\nTAVG\nGHCND:USW00014732\nH,,S,\n23.4\n23\n74.12\n74\n\n\n1\n2018-10-11\nTMAX\nGHCND:USW00014732\n,,W,2400\n26.7\n26\n80.06\n80\n\n\n2\n2018-10-11\nTMIN\nGHCND:USW00014732\n,,W,2400\n21.7\n21\n71.06\n71\n\n\n3\n2018-10-12\nTAVG\nGHCND:USW00014732\nH,,S,\n18.3\n18\n64.94\n64\n\n\n4\n2018-10-12\nTMAX\nGHCND:USW00014732\n,,W,2400\n22.2\n22\n71.96\n71\n\n\n5\n2018-10-12\nTMIN\nGHCND:USW00014732\n,,W,2400\n12.2\n12\n53.96\n53\n\n\n\n\n\n\n\nReindexing allows us to conform our axis to contain a given set of labels. Let’s turn to the S&P 500 stock data in the sp500.csv file to see an example of this. Notice we only have data for trading days (weekdays, excluding holidays):\n\nsp = pd.read_csv(\n '../data/sp500.csv', index_col='date', parse_dates=True\n).drop(columns=['adj_close'])\n\nsp.head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3770530000\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3764890000\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3761820000\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3339890000\nFriday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3217610000\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3638790000\nTuesday\n\n\n2017-01-11\n2275.320068\n2260.830078\n2268.600098\n2275.320068\n3620410000\nWednesday\n\n\n2017-01-12\n2271.780029\n2254.250000\n2271.139893\n2270.439941\n3462130000\nThursday\n\n\n2017-01-13\n2278.679932\n2271.510010\n2272.739990\n2274.639893\n3081270000\nFriday\n\n\n2017-01-17\n2272.080078\n2262.810059\n2269.139893\n2267.889893\n3584990000\nTuesday\n\n\n\n\n\n\n\nIf we want to look at the value of a portfolio (group of assets) that trade on different days, we need to handle the mismatch in the index. Bitcoin, for example, trades daily. If we sum up all the data we have for each day (aggregations will be covered in chapter 4, so don’t fixate on this part), we get the following:\n\nbitcoin = pd.read_csv(\n '../data/bitcoin.csv', index_col='date', parse_dates=True\n).drop(columns=['market_cap'])\n\n# every day's closing price = S&P 500 close + Bitcoin close (same for other metrics)\nportfolio = pd.concat([sp, bitcoin], sort=False).groupby(level='date').sum()\n\nportfolio.head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\n1003.080000\n958.700000\n963.660000\n998.330000\n147775008\nSunday\n\n\n2017-01-02\n1031.390000\n996.700000\n998.620000\n1021.750000\n222184992\nMonday\n\n\n2017-01-03\n3307.959883\n3266.729883\n3273.170068\n3301.670078\n3955698000\nTuesday\n\n\n2017-01-04\n3432.240068\n3306.000098\n3306.000098\n3425.480000\n4109835984\nWednesday\n\n\n2017-01-05\n3462.600000\n3170.869951\n3424.909932\n3282.380000\n4272019008\nThursday\n\n\n2017-01-06\n3328.910098\n3148.000059\n3285.379893\n3179.179980\n3691766000\nFriday\n\n\n2017-01-07\n908.590000\n823.560000\n903.490000\n908.590000\n279550016\nSaturday\n\n\n2017-01-08\n942.720000\n887.250000\n908.170000\n911.200000\n158715008\nSunday\n\n\n2017-01-09\n3189.179990\n3148.709902\n3186.830088\n3171.729902\n3359486992\nMonday\n\n\n2017-01-10\n3194.140020\n3166.330020\n3172.159971\n3176.579902\n3754598000\nTuesday\n\n\n\n\n\n\n\nIt may not be immediately obvious what is wrong with the previous data, but with a visualization we can easily see the cyclical pattern of drops on the days the stock market is closed. (Don’t worry about the plotting code too much, we will cover it in depth in chapters 5 and 6).\nWe will need to import matplotlib now:\n\nimport matplotlib.pyplot as plt # we use this module for plotting\nfrom matplotlib.ticker import StrMethodFormatter # for formatting the axis\n\nNow we can see why we need to reindex:\n\n# plot the closing price from Q4 2017 through Q2 2018\nax = portfolio['2017-Q4':'2018-Q2'].plot(\n y='close', figsize=(15, 5), legend=False,\n title='Bitcoin + S&P 500 value without accounting for different indices'\n)\n\n# formatting\nax.set_ylabel('price')\nax.yaxis.set_major_formatter(StrMethodFormatter('${x:,.0f}'))\nfor spine in ['top', 'right']:\n ax.spines[spine].set_visible(False)\n\n# show the plot\nplt.show()\n\n\n\n\n\n\n\n\nWe need to align the index of the S&P 500 to match bitcoin in order to fix this. We will use the reindex() method, but by default we get NaN for the values that we don’t have data for:\n\nsp.reindex(bitcoin.index).head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\nNaN\nNaN\nNaN\nNaN\nNaN\nSunday\n\n\n2017-01-02\nNaN\nNaN\nNaN\nNaN\nNaN\nMonday\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3.770530e+09\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3.764890e+09\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3.761820e+09\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nFriday\n\n\n2017-01-07\nNaN\nNaN\nNaN\nNaN\nNaN\nSaturday\n\n\n2017-01-08\nNaN\nNaN\nNaN\nNaN\nNaN\nSunday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3.217610e+09\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3.638790e+09\nTuesday\n\n\n\n\n\n\n\nSo now we have rows for every day of the year, but all the weekends and holidays have NaN values. To address this, we can specify how to handle missing values with the method argument. In this case, we want to forward-fill, which will put the weekend and holiday values as the value they had for the Friday (or end of trading week) before:\n\nsp.reindex(bitcoin.index, method='ffill').head(10)\\\n .assign(day_of_week=lambda x: x.index.day_name())\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\nNaN\nNaN\nNaN\nNaN\nNaN\nSunday\n\n\n2017-01-02\nNaN\nNaN\nNaN\nNaN\nNaN\nMonday\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3.770530e+09\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3.764890e+09\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3.761820e+09\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nFriday\n\n\n2017-01-07\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nSaturday\n\n\n2017-01-08\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nSunday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3.217610e+09\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3.638790e+09\nTuesday\n\n\n\n\n\n\n\nTo isolate the changes happening with the forward-filling, we can use the compare() method. It shows us the values that differ across identically-labeled dataframes (same names and same columns). Here, we can see that only weekends and holidays (Monday, January 16, 2017 was MLK day) have values forward-filled. Notice that consecutive days have the same values.\n\nsp.reindex(bitcoin.index)\\\n .compare(sp.reindex(bitcoin.index, method='ffill'))\\\n .head(10).assign(day_of_week=lambda x: x.index.day_name())\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\n\nself\nother\nself\nother\nself\nother\nself\nother\nself\nother\n\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n2017-01-07\nNaN\n2282.100098\nNaN\n2264.060059\nNaN\n2271.139893\nNaN\n2276.979980\nNaN\n3.339890e+09\nSaturday\n\n\n2017-01-08\nNaN\n2282.100098\nNaN\n2264.060059\nNaN\n2271.139893\nNaN\n2276.979980\nNaN\n3.339890e+09\nSunday\n\n\n2017-01-14\nNaN\n2278.679932\nNaN\n2271.510010\nNaN\n2272.739990\nNaN\n2274.639893\nNaN\n3.081270e+09\nSaturday\n\n\n2017-01-15\nNaN\n2278.679932\nNaN\n2271.510010\nNaN\n2272.739990\nNaN\n2274.639893\nNaN\n3.081270e+09\nSunday\n\n\n2017-01-16\nNaN\n2278.679932\nNaN\n2271.510010\nNaN\n2272.739990\nNaN\n2274.639893\nNaN\n3.081270e+09\nMonday\n\n\n2017-01-21\nNaN\n2276.959961\nNaN\n2265.010010\nNaN\n2269.959961\nNaN\n2271.310059\nNaN\n3.524970e+09\nSaturday\n\n\n2017-01-22\nNaN\n2276.959961\nNaN\n2265.010010\nNaN\n2269.959961\nNaN\n2271.310059\nNaN\n3.524970e+09\nSunday\n\n\n2017-01-28\nNaN\n2299.020020\nNaN\n2291.620117\nNaN\n2299.020020\nNaN\n2294.689941\nNaN\n3.135890e+09\nSaturday\n\n\n2017-01-29\nNaN\n2299.020020\nNaN\n2291.620117\nNaN\n2299.020020\nNaN\n2294.689941\nNaN\n3.135890e+09\nSunday\n\n\n2017-02-04\nNaN\n2298.310059\nNaN\n2287.879883\nNaN\n2288.540039\nNaN\n2297.419922\nNaN\n3.597970e+09\nSaturday\n\n\n\n\n\n\n\nThis isn’t perfect though. We probably want 0 for the volume traded and to put the closing price for the open, high, low, and close on the days the market is closed:\nThe reason why we’re using np.where(boolean condition, value if True, value if False) within lambda functions in the example below, is that vectorized operations allow us to be faster and more efficient than utilizing for loops to perform calculations on arrays all at once.\n\nimport numpy as np\n\nsp_reindexed = sp.reindex(bitcoin.index).assign(\n volume=lambda x: x.volume.fillna(0), # put 0 when market is closed\n close=lambda x: x.close.fillna(method='ffill'), # carry this forward\n # take the closing price if these aren't available\n open=lambda x: np.where(x.open.isnull(), x.close, x.open),\n high=lambda x: np.where(x.high.isnull(), x.close, x.high),\n low=lambda x: np.where(x.low.isnull(), x.close, x.low)\n)\nsp_reindexed.head(10).assign(\n day_of_week=lambda x: x.index.day_name()\n)\n\n\n\n\n\n\n\n\nhigh\nlow\nopen\nclose\nvolume\nday_of_week\n\n\ndate\n\n\n\n\n\n\n\n\n\n\n2017-01-01\nNaN\nNaN\nNaN\nNaN\n0.000000e+00\nSunday\n\n\n2017-01-02\nNaN\nNaN\nNaN\nNaN\n0.000000e+00\nMonday\n\n\n2017-01-03\n2263.879883\n2245.129883\n2251.570068\n2257.830078\n3.770530e+09\nTuesday\n\n\n2017-01-04\n2272.820068\n2261.600098\n2261.600098\n2270.750000\n3.764890e+09\nWednesday\n\n\n2017-01-05\n2271.500000\n2260.449951\n2268.179932\n2269.000000\n3.761820e+09\nThursday\n\n\n2017-01-06\n2282.100098\n2264.060059\n2271.139893\n2276.979980\n3.339890e+09\nFriday\n\n\n2017-01-07\n2276.979980\n2276.979980\n2276.979980\n2276.979980\n0.000000e+00\nSaturday\n\n\n2017-01-08\n2276.979980\n2276.979980\n2276.979980\n2276.979980\n0.000000e+00\nSunday\n\n\n2017-01-09\n2275.489990\n2268.899902\n2273.590088\n2268.899902\n3.217610e+09\nMonday\n\n\n2017-01-10\n2279.270020\n2265.270020\n2269.719971\n2268.899902\n3.638790e+09\nTuesday\n\n\n\n\n\n\n\nIf we create a visualization comparing the reindexed data to the first attempt, we see how reindexing helped maintain the asset value when the market was closed:\n\n# every day's closing price = S&P 500 close adjusted for market closure + Bitcoin close (same for other metrics)\nfixed_portfolio = sp_reindexed + bitcoin\n\n# plot the reindexed portfolio's closing price from Q4 2017 through Q2 2018\nax = fixed_portfolio['2017-Q4':'2018-Q2'].plot(\n y='close', label='reindexed portfolio of S&P 500 + Bitcoin', figsize=(15, 5), linewidth=2, \n title='Reindexed portfolio vs. portfolio with mismatched indices'\n)\n\n# add line for original portfolio for comparison\nportfolio['2017-Q4':'2018-Q2'].plot(\n y='close', ax=ax, linestyle='--', label='portfolio of S&P 500 + Bitcoin w/o reindexing'\n)\n\n# formatting\nax.set_ylabel('price')\nax.yaxis.set_major_formatter(StrMethodFormatter('${x:,.0f}'))\nfor spine in ['top', 'right']:\n ax.spines[spine].set_visible(False)\n\n# show the plot\nplt.show()" + }, + { + "objectID": "notebooks/hypothesis_testing.html", + "href": "notebooks/hypothesis_testing.html", + "title": "Hypothesis Testing", + "section": "", + "text": "Hypothesis Testing\nA hypothesis is a claim or statement about a parameter1. There are two types of statistical hypotheses: - Null Hypothesis - Alternative Hypothesis\nA null hypothesis is a statement that claims that the parameter is equal to some claimed value. - H0 is the symbol used to denote this. It can be pronounced as “H null”, “H zero” or “H nought” - It always contains one of these operators: \\(\\ge\\), \\(\\le\\), =. - This value is the one to always assume is true.\nAn alternative hypothesis is a statement that claims that the parameter is a different value than the null. - HA or H1 is the symbol used to denote this. It’s always called “alternative hypothesis.” - It always contains one of these operators: \\(\\gt\\), \\(\\lt\\), \\(\\neq\\).\n\nSteps to Solving a Hypothesis Test Problem\n\nWrite and label everything.\nWrite hypotheses:\n\nH0: (operator with equal sign)\nHA: (operator without equal sign)\n\nDraw graph (bell-curved)\n\nThe graph will either be right, left or two tailed.\n\nCarry out the necessary calculations to arrive to a solution.\n\nThis can involve solving a t-statisic or z-test.\n\nWrite a sentence summarizing the findings.\n\nUsually follows this format: “There is/is not sufficient evidence to support/reject the claim that…”\n\n\n\n\nRejection Explained\nEvery hypothesis test is rejected or failed to reject. This is because we either have enough data to be able to say the hypothesis is correct, or we don’t have enough data to prove otherwise. To determine this, we compare the significance level to the p-value .\nThe significance level is denoted by \\(\\alpha\\) which measures how strong the evidence must be in order to determine the evidence to be statistically significant.\nP-value is defined by Investopedia as “a statistical measurement used to validate a hypothesis against observed data.” We’re not going to go in-depth here regarding how the p-value is calculated, but just enough to scratch the surface. This value describes the likelihood of the data occurring randomly. P-values range from 0 to 1 and a smaller p-value denotes a smaller probability that the results occurred randomly.\n\nIf p-value \\(\\leq\\) \\(\\alpha\\), then reject H0.\nIf p-value \\(\\gt\\) \\(\\alpha\\), then fail to reject H0.\n\n\n\nDetermining the Tail of the Curve\nThe trick to remembering where the tail of the curve is by looking at the alternative hypothesis. - If the sign in HA is - \\(\\neq\\): two-tailed - \\(\\lt\\): left-taied - \\(\\gt\\): right-tailed\n\n\nErrors\nSometimes, error occurs with hypothesis testing and there are two types of it: - Type I error - This is known as the false-positive. - It occurs when the null hypothesis is rejected, but it is true. - Type II error - This is known as the false-negative. - It occurs when the null hypothesis is not rejected, but it is false.\nThis table below from Scribbr can be used to determine error type, if any.\n\n1A parameter is a measure done on an entire population of data." + }, + { + "objectID": "notebooks/introducing_matplotlib.html", + "href": "notebooks/introducing_matplotlib.html", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "Pandas uses matplotlib to create visualizations. Therefore, before we learn how to plot with pandas, it’s important to understand how matplotlib works at a high-level, which is the focus of this notebook.\n\n\nIn this notebook, we will be working with 2 datasets: - Facebook’s stock price throughout 2018 (obtained using the stock_analysis package) - Earthquake data from September 18, 2018 - October 13, 2018 (obtained from the US Geological Survey (USGS) using the USGS API)\n\n\n\nWe need to import matplotlib.pyplot for plotting.\n\nimport matplotlib.pyplot as plt\nimport pandas as pd\n\n\n\n\n\nfb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)\n\nplt.plot(fb.index, fb.open)\nplt.show()\n\n\n\n\n\n\n\n\nSince we are working in a Jupyter notebook, we can use the magic command %matplotlib inline once and not have to call plt.show() for each plot.\n\n%matplotlib inline\nimport matplotlib.pyplot as plt\nimport pandas as pd\n\nfb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)\nplt.plot(fb.index, fb.open)\n\n\n\n\n\n\n\n\n\n\n\nWe can pass in a string specifying the style of the plot. This is of the form [marker][linestyle][color]. For example, we can make a black dashed line with '--k' or a red scatter plot with 'or':\n\nplt.plot('high', 'low', 'or', data=fb.head(20))\n\n\n\n\n\n\n\n\nHere are some examples of how you make a format string:\n\n\n\nMarker\nLinestyle\nColor\nFormat String\nResult\n\n\n\n\n\n-\nb\n-b\nblue solid line\n\n\n.\n\nk\n.k\nblack points\n\n\n\n--\nr\n--r\nred dashed line\n\n\no\n-\ng\no-g\ngreen solid line with circles\n\n\n\n:\nm\n:m\nmagenta dotted line\n\n\nx\n-.\nc\nx-.c\ncyan dot-dashed line with x’s\n\n\n\nNote that we can also use format strings of the form [color][marker][linestyle], but the parsing by matplotlib (in rare cases) might not be what we were aiming for. Consult the Notes section in the documentation for the complete list of options. ## Histograms\n\nquakes = pd.read_csv('../data/earthquakes.csv')\nplt.hist(quakes.query('magType == \"ml\"').mag)\n\n(array([6.400e+01, 4.450e+02, 1.137e+03, 1.853e+03, 2.114e+03, 8.070e+02,\n 2.800e+02, 9.200e+01, 9.000e+00, 2.000e+00]),\n array([-1.26 , -0.624, 0.012, 0.648, 1.284, 1.92 , 2.556, 3.192,\n 3.828, 4.464, 5.1 ]),\n <BarContainer object of 10 artists>)\n\n\n\n\n\n\n\n\n\n\n\nNotice how our assumptions of the distribution of the data can change based on the number of bins (look at the drop between the two highest peaks on the righthand plot):\n\nx = quakes.query('magType == \"ml\"').mag\nfig, axes = plt.subplots(1, 2, figsize=(10, 3))\nfor ax, bins in zip(axes, [7, 35]):\n ax.hist(x, bins=bins)\n ax.set_title(f'bins param: {bins}')\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nTop-level object that holds the other plot components.\n\nfig = plt.figure()\n\n<Figure size 640x480 with 0 Axes>\n\n\n\n\n\nIndividual plots contained within the Figure.\n\n\n\n\nSimply specify the number of rows and columns to create:\n\nfig, axes = plt.subplots(1, 2)\n\n\n\n\n\n\n\n\nAs an alternative to using plt.subplots() we can add Axes objects to the Figure object on our own. This allows for some more complex layouts, such as picture in picture:\n\nfig = plt.figure(figsize=(3, 3))\noutside = fig.add_axes([0.1, 0.1, 0.9, 0.9])\ninside = fig.add_axes([0.7, 0.7, 0.25, 0.25])\n\n\n\n\n\n\n\n\n\n\n\nWe can create subplots with varying sizes as well:\n\nfig = plt.figure(figsize=(8, 8))\ngs = fig.add_gridspec(3, 3)\ntop_left = fig.add_subplot(gs[0, 0])\nmid_left = fig.add_subplot(gs[1, 0])\ntop_right = fig.add_subplot(gs[:2, 1:])\nbottom = fig.add_subplot(gs[2,:])\n\n\n\n\n\n\n\n\n\n\n\nUse plt.savefig() to save the last created plot. To save a specific Figure object, use its savefig() method. Which supports ‘png’, ‘pdf’, ‘svg’, and ‘eps’ filetypes.\n\nfig.savefig('empty.png')\nfig.savefig('empty.pdf')\nfig.savefig('empty.svg')\nfig.savefig('empty.eps')\n\n\n\n\nIt’s important to close resources when we are done with them. We use plt.close() to do so. If we pass in nothing, it will close the last plot, but we can pass in the specific Figure object to close or say 'all' to close all Figure objects that are open. Let’s close all the Figure objects that are open with plt.close():\n\nplt.close('all')\n\n\n\n\n\n\nJust pass the figsize argument to plt.figure(). It’s a tuple of (width, height):\n\nfig = plt.figure(figsize=(10, 4))\n\n<Figure size 1000x400 with 0 Axes>\n\n\nThis can be specified when creating subplots as well:\n\nfig, axes = plt.subplots(1, 2, figsize=(10, 4))\n\n\n\n\n\n\n\n\n\n\n\nA small subset of all the available plot settings (shuffling to get a good variation of options):\n\nimport random\nimport matplotlib as mpl\n\nrcparams_list = list(mpl.rcParams.keys())\nrandom.seed(20) # make this repeatable\nrandom.shuffle(rcparams_list)\nsorted(rcparams_list[:20])\n\n['animation.convert_args',\n 'axes.edgecolor',\n 'axes.formatter.use_locale',\n 'axes.spines.right',\n 'boxplot.meanprops.markersize',\n 'boxplot.showfliers',\n 'keymap.home',\n 'lines.markerfacecolor',\n 'lines.scale_dashes',\n 'mathtext.rm',\n 'patch.force_edgecolor',\n 'savefig.facecolor',\n 'svg.fonttype',\n 'text.hinting_factor',\n 'xtick.alignment',\n 'xtick.minor.top',\n 'xtick.minor.width',\n 'ytick.left',\n 'ytick.major.left',\n 'ytick.minor.width']\n\n\nWe can check the current default figsize using rcParams:\n\nmpl.rcParams['figure.figsize']\n\n[6.4, 4.8]\n\n\nWe can also update this value to change the default (until the kernel is restarted):\n\nmpl.rcParams['figure.figsize'] = (300, 10)\nmpl.rcParams['figure.figsize']\n\n[300.0, 10.0]\n\n\nUse rcdefaults() to restore the defaults. Note this is slightly different than before because running %matplotlib inline sets a different value for figsize (see more). After we reset, we are going back to the default value of figsize before that import:\n\nmpl.rcdefaults()\nmpl.rcParams['figure.figsize']\n\n[6.4, 4.8]\n\n\nThis can also be done via pyplot:\n\nplt.rc('figure', figsize=(20, 20)) # change `figsize` default to (20, 20)\nplt.rcdefaults() # reset the default" + }, + { + "objectID": "notebooks/introducing_matplotlib.html#about-the-data", + "href": "notebooks/introducing_matplotlib.html#about-the-data", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "In this notebook, we will be working with 2 datasets: - Facebook’s stock price throughout 2018 (obtained using the stock_analysis package) - Earthquake data from September 18, 2018 - October 13, 2018 (obtained from the US Geological Survey (USGS) using the USGS API)" + }, + { + "objectID": "notebooks/introducing_matplotlib.html#setup", + "href": "notebooks/introducing_matplotlib.html#setup", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "We need to import matplotlib.pyplot for plotting.\n\nimport matplotlib.pyplot as plt\nimport pandas as pd" + }, + { + "objectID": "notebooks/introducing_matplotlib.html#plotting-lines", + "href": "notebooks/introducing_matplotlib.html#plotting-lines", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "fb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)\n\nplt.plot(fb.index, fb.open)\nplt.show()\n\n\n\n\n\n\n\n\nSince we are working in a Jupyter notebook, we can use the magic command %matplotlib inline once and not have to call plt.show() for each plot.\n\n%matplotlib inline\nimport matplotlib.pyplot as plt\nimport pandas as pd\n\nfb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)\nplt.plot(fb.index, fb.open)" + }, + { + "objectID": "notebooks/introducing_matplotlib.html#scatter-plots", + "href": "notebooks/introducing_matplotlib.html#scatter-plots", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "We can pass in a string specifying the style of the plot. This is of the form [marker][linestyle][color]. For example, we can make a black dashed line with '--k' or a red scatter plot with 'or':\n\nplt.plot('high', 'low', 'or', data=fb.head(20))\n\n\n\n\n\n\n\n\nHere are some examples of how you make a format string:\n\n\n\nMarker\nLinestyle\nColor\nFormat String\nResult\n\n\n\n\n\n-\nb\n-b\nblue solid line\n\n\n.\n\nk\n.k\nblack points\n\n\n\n--\nr\n--r\nred dashed line\n\n\no\n-\ng\no-g\ngreen solid line with circles\n\n\n\n:\nm\n:m\nmagenta dotted line\n\n\nx\n-.\nc\nx-.c\ncyan dot-dashed line with x’s\n\n\n\nNote that we can also use format strings of the form [color][marker][linestyle], but the parsing by matplotlib (in rare cases) might not be what we were aiming for. Consult the Notes section in the documentation for the complete list of options. ## Histograms\n\nquakes = pd.read_csv('../data/earthquakes.csv')\nplt.hist(quakes.query('magType == \"ml\"').mag)\n\n(array([6.400e+01, 4.450e+02, 1.137e+03, 1.853e+03, 2.114e+03, 8.070e+02,\n 2.800e+02, 9.200e+01, 9.000e+00, 2.000e+00]),\n array([-1.26 , -0.624, 0.012, 0.648, 1.284, 1.92 , 2.556, 3.192,\n 3.828, 4.464, 5.1 ]),\n <BarContainer object of 10 artists>)\n\n\n\n\n\n\n\n\n\n\n\nNotice how our assumptions of the distribution of the data can change based on the number of bins (look at the drop between the two highest peaks on the righthand plot):\n\nx = quakes.query('magType == \"ml\"').mag\nfig, axes = plt.subplots(1, 2, figsize=(10, 3))\nfor ax, bins in zip(axes, [7, 35]):\n ax.hist(x, bins=bins)\n ax.set_title(f'bins param: {bins}')" + }, + { + "objectID": "notebooks/introducing_matplotlib.html#plot-components", + "href": "notebooks/introducing_matplotlib.html#plot-components", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "Top-level object that holds the other plot components.\n\nfig = plt.figure()\n\n<Figure size 640x480 with 0 Axes>\n\n\n\n\n\nIndividual plots contained within the Figure." + }, + { + "objectID": "notebooks/introducing_matplotlib.html#creating-subplots", + "href": "notebooks/introducing_matplotlib.html#creating-subplots", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "Simply specify the number of rows and columns to create:\n\nfig, axes = plt.subplots(1, 2)\n\n\n\n\n\n\n\n\nAs an alternative to using plt.subplots() we can add Axes objects to the Figure object on our own. This allows for some more complex layouts, such as picture in picture:\n\nfig = plt.figure(figsize=(3, 3))\noutside = fig.add_axes([0.1, 0.1, 0.9, 0.9])\ninside = fig.add_axes([0.7, 0.7, 0.25, 0.25])" + }, + { + "objectID": "notebooks/introducing_matplotlib.html#creating-plot-layouts-with-gridspec", + "href": "notebooks/introducing_matplotlib.html#creating-plot-layouts-with-gridspec", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "We can create subplots with varying sizes as well:\n\nfig = plt.figure(figsize=(8, 8))\ngs = fig.add_gridspec(3, 3)\ntop_left = fig.add_subplot(gs[0, 0])\nmid_left = fig.add_subplot(gs[1, 0])\ntop_right = fig.add_subplot(gs[:2, 1:])\nbottom = fig.add_subplot(gs[2,:])" + }, + { + "objectID": "notebooks/introducing_matplotlib.html#saving-plots", + "href": "notebooks/introducing_matplotlib.html#saving-plots", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "Use plt.savefig() to save the last created plot. To save a specific Figure object, use its savefig() method. Which supports ‘png’, ‘pdf’, ‘svg’, and ‘eps’ filetypes.\n\nfig.savefig('empty.png')\nfig.savefig('empty.pdf')\nfig.savefig('empty.svg')\nfig.savefig('empty.eps')" + }, + { + "objectID": "notebooks/introducing_matplotlib.html#cleaning-up", + "href": "notebooks/introducing_matplotlib.html#cleaning-up", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "It’s important to close resources when we are done with them. We use plt.close() to do so. If we pass in nothing, it will close the last plot, but we can pass in the specific Figure object to close or say 'all' to close all Figure objects that are open. Let’s close all the Figure objects that are open with plt.close():\n\nplt.close('all')" + }, + { + "objectID": "notebooks/introducing_matplotlib.html#additional-plotting-options", + "href": "notebooks/introducing_matplotlib.html#additional-plotting-options", + "title": "Getting Started with Matplotlib", + "section": "", + "text": "Just pass the figsize argument to plt.figure(). It’s a tuple of (width, height):\n\nfig = plt.figure(figsize=(10, 4))\n\n<Figure size 1000x400 with 0 Axes>\n\n\nThis can be specified when creating subplots as well:\n\nfig, axes = plt.subplots(1, 2, figsize=(10, 4))\n\n\n\n\n\n\n\n\n\n\n\nA small subset of all the available plot settings (shuffling to get a good variation of options):\n\nimport random\nimport matplotlib as mpl\n\nrcparams_list = list(mpl.rcParams.keys())\nrandom.seed(20) # make this repeatable\nrandom.shuffle(rcparams_list)\nsorted(rcparams_list[:20])\n\n['animation.convert_args',\n 'axes.edgecolor',\n 'axes.formatter.use_locale',\n 'axes.spines.right',\n 'boxplot.meanprops.markersize',\n 'boxplot.showfliers',\n 'keymap.home',\n 'lines.markerfacecolor',\n 'lines.scale_dashes',\n 'mathtext.rm',\n 'patch.force_edgecolor',\n 'savefig.facecolor',\n 'svg.fonttype',\n 'text.hinting_factor',\n 'xtick.alignment',\n 'xtick.minor.top',\n 'xtick.minor.width',\n 'ytick.left',\n 'ytick.major.left',\n 'ytick.minor.width']\n\n\nWe can check the current default figsize using rcParams:\n\nmpl.rcParams['figure.figsize']\n\n[6.4, 4.8]\n\n\nWe can also update this value to change the default (until the kernel is restarted):\n\nmpl.rcParams['figure.figsize'] = (300, 10)\nmpl.rcParams['figure.figsize']\n\n[300.0, 10.0]\n\n\nUse rcdefaults() to restore the defaults. Note this is slightly different than before because running %matplotlib inline sets a different value for figsize (see more). After we reset, we are going back to the default value of figsize before that import:\n\nmpl.rcdefaults()\nmpl.rcParams['figure.figsize']\n\n[6.4, 4.8]\n\n\nThis can also be done via pyplot:\n\nplt.rc('figure', figsize=(20, 20)) # change `figsize` default to (20, 20)\nplt.rcdefaults() # reset the default" + }, + { + "objectID": "notebooks/making_dataframes_from_api_requests.html", + "href": "notebooks/making_dataframes_from_api_requests.html", + "title": "Making Pandas DataFrames from API Requests", + "section": "", + "text": "Making Pandas DataFrames from API Requests\nIn this example, we will use the U.S. Geological Survey’s API to grab a JSON object of earthquake data and convert it to a pandas.DataFrame.\nUSGS API: https://earthquake.usgs.gov/fdsnws/event/1/\n\nGet Data from API\n\nimport datetime as dt\nimport pandas as pd\nimport requests\n\nyesterday = dt.date.today() - dt.timedelta(days=1)\napi = 'https://earthquake.usgs.gov/fdsnws/event/1/query'\npayload = {\n 'format': 'geojson',\n 'starttime': yesterday - dt.timedelta(days=30),\n 'endtime': yesterday\n}\nresponse = requests.get(api, params=payload)\n\n# let's make sure the request was OK\nresponse.status_code\n\n200\n\n\nResponse of 200 means OK, so we can pull the data out of the result. Since we asked the API for a JSON payload, we can extract it from the response with the json() method.\n\n\nIsolate the Data from the JSON Response\nWe need to check the structures of the response data to know where our data is.\n\nearthquake_json = response.json()\nearthquake_json.keys()\n\ndict_keys(['type', 'metadata', 'features', 'bbox'])\n\n\nThe USGS API provides information about our request in the metadata key. Note that your result will be different, regardless of the date range you chose, because the API includes a timestamp for when the data was pulled:\n\nearthquake_json['metadata']\n\n{'generated': 1686247399000,\n 'url': 'https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2023-05-08&endtime=2023-06-07',\n 'title': 'USGS Earthquakes',\n 'status': 200,\n 'api': '1.14.0',\n 'count': 11706}\n\n\nEach element in the JSON array features is a row of data for our dataframe.\n\ntype(earthquake_json['features'])\n\nlist\n\n\nYour data will be different depending on the date you run this.\n\nearthquake_json['features'][0]\n\n{'type': 'Feature',\n 'properties': {'mag': 0.5,\n 'place': '17km SE of Anza, CA',\n 'time': 1686095356120,\n 'updated': 1686178472172,\n 'tz': None,\n 'url': 'https://earthquake.usgs.gov/earthquakes/eventpage/ci40479680',\n 'detail': 'https://earthquake.usgs.gov/fdsnws/event/1/query?eventid=ci40479680&format=geojson',\n 'felt': None,\n 'cdi': None,\n 'mmi': None,\n 'alert': None,\n 'status': 'reviewed',\n 'tsunami': 0,\n 'sig': 4,\n 'net': 'ci',\n 'code': '40479680',\n 'ids': ',ci40479680,',\n 'sources': ',ci,',\n 'types': ',nearby-cities,origin,phase-data,scitech-link,',\n 'nst': 21,\n 'dmin': 0.07016,\n 'rms': 0.26,\n 'gap': 86,\n 'magType': 'ml',\n 'type': 'earthquake',\n 'title': 'M 0.5 - 17km SE of Anza, CA'},\n 'geometry': {'type': 'Point', 'coordinates': [-116.5405, 33.447, 11.37]},\n 'id': 'ci40479680'}\n\n\n\n\nConvert to DataFrame\nWe need to grab the properties section out of every entry in the features JSON array to create our dataframe.\n\nearthquake_properties_data = [\n quake['properties'] for quake in earthquake_json['features']\n]\ndf = pd.DataFrame(earthquake_properties_data)\ndf.head()\n\n\n\n\n\n\n\n\nmag\nplace\ntime\nupdated\ntz\nurl\ndetail\nfelt\ncdi\nmmi\n...\nids\nsources\ntypes\nnst\ndmin\nrms\ngap\nmagType\ntype\ntitle\n\n\n\n\n0\n0.50\n17km SE of Anza, CA\n1686095356120\n1686178472172\nNone\nhttps://earthquake.usgs.gov/earthquakes/eventp...\nhttps://earthquake.usgs.gov/fdsnws/event/1/que...\nNaN\nNaN\nNaN\n...\n,ci40479680,\n,ci,\n,nearby-cities,origin,phase-data,scitech-link,\n21.0\n0.070160\n0.26\n86.0\nml\nearthquake\nM 0.5 - 17km SE of Anza, CA\n\n\n1\n1.24\n6 km W of Blanchard, Oklahoma\n1686095288900\n1686140204481\nNone\nhttps://earthquake.usgs.gov/earthquakes/eventp...\nhttps://earthquake.usgs.gov/fdsnws/event/1/que...\n0.0\n1.0\nNaN\n...\n,ok2023lavp,\n,ok,\n,dyfi,origin,phase-data,\n64.0\n0.089083\n0.44\n42.0\nml\nearthquake\nM 1.2 - 6 km W of Blanchard, Oklahoma\n\n\n2\n5.20\nsoutheast of the Loyalty Islands\n1686094693572\n1686095577040\nNone\nhttps://earthquake.usgs.gov/earthquakes/eventp...\nhttps://earthquake.usgs.gov/fdsnws/event/1/que...\nNaN\nNaN\nNaN\n...\n,us7000k6rg,\n,us,\n,origin,phase-data,\n32.0\n2.755000\n0.82\n85.0\nmb\nearthquake\nM 5.2 - southeast of the Loyalty Islands\n\n\n3\n0.24\n10 km NNE of Government Camp, Oregon\n1686094611590\n1686118209350\nNone\nhttps://earthquake.usgs.gov/earthquakes/eventp...\nhttps://earthquake.usgs.gov/fdsnws/event/1/que...\nNaN\nNaN\nNaN\n...\n,uw61931036,\n,uw,\n,origin,phase-data,\n3.0\n0.032620\n0.04\n239.0\nml\nearthquake\nM 0.2 - 10 km NNE of Government Camp, Oregon\n\n\n4\n-0.01\n8 km NNE of Government Camp, Oregon\n1686094117310\n1686117953180\nNone\nhttps://earthquake.usgs.gov/earthquakes/eventp...\nhttps://earthquake.usgs.gov/fdsnws/event/1/que...\nNaN\nNaN\nNaN\n...\n,uw61931031,\n,uw,\n,origin,phase-data,\n4.0\n0.011510\n0.04\n173.0\nml\nearthquake\nM 0.0 - 8 km NNE of Government Camp, Oregon\n\n\n\n\n5 rows × 26 columns\n\n\n\n\n\n(Optional) Write Data to CSV\n\ndf.to_csv('earthquakes.csv', index=False)" + }, + { + "objectID": "notebooks/pandas_plotting_module.html", + "href": "notebooks/pandas_plotting_module.html", + "title": "The pandas.plotting module", + "section": "", + "text": "Pandas provides some extra plotting functions for some new plot types.\n\n\nIn this notebook, we will be working with Facebook’s stock price throughout 2018 (obtained using the stock_analysis package).\n\n\n\n\n%matplotlib inline\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)\n\n\n\n\nEasily create scatter plots between all columns in the dataset:\n\nfrom pandas.plotting import scatter_matrix\nscatter_matrix(fb, figsize=(10, 10))\n\narray([[<Axes: xlabel='open', ylabel='open'>,\n <Axes: xlabel='high', ylabel='open'>,\n <Axes: xlabel='low', ylabel='open'>,\n <Axes: xlabel='close', ylabel='open'>,\n <Axes: xlabel='volume', ylabel='open'>],\n [<Axes: xlabel='open', ylabel='high'>,\n <Axes: xlabel='high', ylabel='high'>,\n <Axes: xlabel='low', ylabel='high'>,\n <Axes: xlabel='close', ylabel='high'>,\n <Axes: xlabel='volume', ylabel='high'>],\n [<Axes: xlabel='open', ylabel='low'>,\n <Axes: xlabel='high', ylabel='low'>,\n <Axes: xlabel='low', ylabel='low'>,\n <Axes: xlabel='close', ylabel='low'>,\n <Axes: xlabel='volume', ylabel='low'>],\n [<Axes: xlabel='open', ylabel='close'>,\n <Axes: xlabel='high', ylabel='close'>,\n <Axes: xlabel='low', ylabel='close'>,\n <Axes: xlabel='close', ylabel='close'>,\n <Axes: xlabel='volume', ylabel='close'>],\n [<Axes: xlabel='open', ylabel='volume'>,\n <Axes: xlabel='high', ylabel='volume'>,\n <Axes: xlabel='low', ylabel='volume'>,\n <Axes: xlabel='close', ylabel='volume'>,\n <Axes: xlabel='volume', ylabel='volume'>]], dtype=object)\n\n\n\n\n\n\n\n\n\nChanging the diagonal from histograms to KDE:\n\nscatter_matrix(fb, figsize=(10, 10), diagonal='kde')\n\narray([[<Axes: xlabel='open', ylabel='open'>,\n <Axes: xlabel='high', ylabel='open'>,\n <Axes: xlabel='low', ylabel='open'>,\n <Axes: xlabel='close', ylabel='open'>,\n <Axes: xlabel='volume', ylabel='open'>],\n [<Axes: xlabel='open', ylabel='high'>,\n <Axes: xlabel='high', ylabel='high'>,\n <Axes: xlabel='low', ylabel='high'>,\n <Axes: xlabel='close', ylabel='high'>,\n <Axes: xlabel='volume', ylabel='high'>],\n [<Axes: xlabel='open', ylabel='low'>,\n <Axes: xlabel='high', ylabel='low'>,\n <Axes: xlabel='low', ylabel='low'>,\n <Axes: xlabel='close', ylabel='low'>,\n <Axes: xlabel='volume', ylabel='low'>],\n [<Axes: xlabel='open', ylabel='close'>,\n <Axes: xlabel='high', ylabel='close'>,\n <Axes: xlabel='low', ylabel='close'>,\n <Axes: xlabel='close', ylabel='close'>,\n <Axes: xlabel='volume', ylabel='close'>],\n [<Axes: xlabel='open', ylabel='volume'>,\n <Axes: xlabel='high', ylabel='volume'>,\n <Axes: xlabel='low', ylabel='volume'>,\n <Axes: xlabel='close', ylabel='volume'>,\n <Axes: xlabel='volume', ylabel='volume'>]], dtype=object)\n\n\n\n\n\n\n\n\n\n\n\n\nLag plots let us see how the variable correlates with past observations of itself. Random data has no pattern:\n\nfrom pandas.plotting import lag_plot\nnp.random.seed(0) # make this repeatable\nlag_plot(pd.Series(np.random.random(size=200)))\n\n\n\n\n\n\n\n\nData with some level of correlation to itself (autocorrelation) may have patterns. Stock prices are highly autocorrelated:\n\nlag_plot(fb.close)\n\n\n\n\n\n\n\n\nThe default lag is 1, but we can alter this with the lag parameter. Let’s look at a 5 day lag (a week of trading activity):\n\nlag_plot(fb.close, lag=5)\n\n\n\n\n\n\n\n\n\n\n\nWe can use the autocorrelation plot to see if this relationship may be meaningful or is just noise. Random data will not have any significant autocorrelation (it stays within the bounds below):\n\nfrom pandas.plotting import autocorrelation_plot\nnp.random.seed(0) # make this repeatable\nautocorrelation_plot(pd.Series(np.random.random(size=200)))\n\n\n\n\n\n\n\n\nStock data, on the other hand, does have significant autocorrelation:\n\nautocorrelation_plot(fb.close)\n\n\n\n\n\n\n\n\n\n\n\nThis plot helps us understand the uncertainty in our summary statistics:\n\nfrom pandas.plotting import bootstrap_plot\nfig = bootstrap_plot(fb.volume, fig=plt.figure(figsize=(10, 6)))" + }, + { + "objectID": "notebooks/pandas_plotting_module.html#about-the-data", + "href": "notebooks/pandas_plotting_module.html#about-the-data", + "title": "The pandas.plotting module", + "section": "", + "text": "In this notebook, we will be working with Facebook’s stock price throughout 2018 (obtained using the stock_analysis package)." + }, + { + "objectID": "notebooks/pandas_plotting_module.html#setup", + "href": "notebooks/pandas_plotting_module.html#setup", + "title": "The pandas.plotting module", + "section": "", + "text": "%matplotlib inline\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfb = pd.read_csv(\n '../data/fb_stock_prices_2018.csv', index_col='date', parse_dates=True\n)" + }, + { + "objectID": "notebooks/pandas_plotting_module.html#scatter-matrix", + "href": "notebooks/pandas_plotting_module.html#scatter-matrix", + "title": "The pandas.plotting module", + "section": "", + "text": "Easily create scatter plots between all columns in the dataset:\n\nfrom pandas.plotting import scatter_matrix\nscatter_matrix(fb, figsize=(10, 10))\n\narray([[<Axes: xlabel='open', ylabel='open'>,\n <Axes: xlabel='high', ylabel='open'>,\n <Axes: xlabel='low', ylabel='open'>,\n <Axes: xlabel='close', ylabel='open'>,\n <Axes: xlabel='volume', ylabel='open'>],\n [<Axes: xlabel='open', ylabel='high'>,\n <Axes: xlabel='high', ylabel='high'>,\n <Axes: xlabel='low', ylabel='high'>,\n <Axes: xlabel='close', ylabel='high'>,\n <Axes: xlabel='volume', ylabel='high'>],\n [<Axes: xlabel='open', ylabel='low'>,\n <Axes: xlabel='high', ylabel='low'>,\n <Axes: xlabel='low', ylabel='low'>,\n <Axes: xlabel='close', ylabel='low'>,\n <Axes: xlabel='volume', ylabel='low'>],\n [<Axes: xlabel='open', ylabel='close'>,\n <Axes: xlabel='high', ylabel='close'>,\n <Axes: xlabel='low', ylabel='close'>,\n <Axes: xlabel='close', ylabel='close'>,\n <Axes: xlabel='volume', ylabel='close'>],\n [<Axes: xlabel='open', ylabel='volume'>,\n <Axes: xlabel='high', ylabel='volume'>,\n <Axes: xlabel='low', ylabel='volume'>,\n <Axes: xlabel='close', ylabel='volume'>,\n <Axes: xlabel='volume', ylabel='volume'>]], dtype=object)\n\n\n\n\n\n\n\n\n\nChanging the diagonal from histograms to KDE:\n\nscatter_matrix(fb, figsize=(10, 10), diagonal='kde')\n\narray([[<Axes: xlabel='open', ylabel='open'>,\n <Axes: xlabel='high', ylabel='open'>,\n <Axes: xlabel='low', ylabel='open'>,\n <Axes: xlabel='close', ylabel='open'>,\n <Axes: xlabel='volume', ylabel='open'>],\n [<Axes: xlabel='open', ylabel='high'>,\n <Axes: xlabel='high', ylabel='high'>,\n <Axes: xlabel='low', ylabel='high'>,\n <Axes: xlabel='close', ylabel='high'>,\n <Axes: xlabel='volume', ylabel='high'>],\n [<Axes: xlabel='open', ylabel='low'>,\n <Axes: xlabel='high', ylabel='low'>,\n <Axes: xlabel='low', ylabel='low'>,\n <Axes: xlabel='close', ylabel='low'>,\n <Axes: xlabel='volume', ylabel='low'>],\n [<Axes: xlabel='open', ylabel='close'>,\n <Axes: xlabel='high', ylabel='close'>,\n <Axes: xlabel='low', ylabel='close'>,\n <Axes: xlabel='close', ylabel='close'>,\n <Axes: xlabel='volume', ylabel='close'>],\n [<Axes: xlabel='open', ylabel='volume'>,\n <Axes: xlabel='high', ylabel='volume'>,\n <Axes: xlabel='low', ylabel='volume'>,\n <Axes: xlabel='close', ylabel='volume'>,\n <Axes: xlabel='volume', ylabel='volume'>]], dtype=object)" + }, + { + "objectID": "notebooks/pandas_plotting_module.html#lag-plot", + "href": "notebooks/pandas_plotting_module.html#lag-plot", + "title": "The pandas.plotting module", + "section": "", + "text": "Lag plots let us see how the variable correlates with past observations of itself. Random data has no pattern:\n\nfrom pandas.plotting import lag_plot\nnp.random.seed(0) # make this repeatable\nlag_plot(pd.Series(np.random.random(size=200)))\n\n\n\n\n\n\n\n\nData with some level of correlation to itself (autocorrelation) may have patterns. Stock prices are highly autocorrelated:\n\nlag_plot(fb.close)\n\n\n\n\n\n\n\n\nThe default lag is 1, but we can alter this with the lag parameter. Let’s look at a 5 day lag (a week of trading activity):\n\nlag_plot(fb.close, lag=5)" + }, + { + "objectID": "notebooks/pandas_plotting_module.html#autocorrelation-plots", + "href": "notebooks/pandas_plotting_module.html#autocorrelation-plots", + "title": "The pandas.plotting module", + "section": "", + "text": "We can use the autocorrelation plot to see if this relationship may be meaningful or is just noise. Random data will not have any significant autocorrelation (it stays within the bounds below):\n\nfrom pandas.plotting import autocorrelation_plot\nnp.random.seed(0) # make this repeatable\nautocorrelation_plot(pd.Series(np.random.random(size=200)))\n\n\n\n\n\n\n\n\nStock data, on the other hand, does have significant autocorrelation:\n\nautocorrelation_plot(fb.close)" + }, + { + "objectID": "notebooks/pandas_plotting_module.html#bootstrap-plot", + "href": "notebooks/pandas_plotting_module.html#bootstrap-plot", + "title": "The pandas.plotting module", + "section": "", + "text": "This plot helps us understand the uncertainty in our summary statistics:\n\nfrom pandas.plotting import bootstrap_plot\nfig = bootstrap_plot(fb.volume, fig=plt.figure(figsize=(10, 6)))" + }, + { + "objectID": "notebooks/python_101.html", + "href": "notebooks/python_101.html", + "title": "Python 101", + "section": "", + "text": "This is an optional notebook to get you up to speed with Python in case you are new to Python or need a refresher. The material here is a crash course in Python; I highly recommend the official Python tutorial for a deeper dive. Consider reading this page in the Python docs for background on Python and bookmarking the glossary.\n\n\n\n\nNumbers in Python can be represented as integers (e.g. 5) or floats (e.g. 5.0). We can perform operations on them:\n\n5 + 6\n\n11\n\n\n\n2.5 / 3\n\n0.8333333333333334\n\n\n\n\n\nWe can check for equality giving us a Boolean:\n\n5 == 6\n\nFalse\n\n\n\n5 < 6\n\nTrue\n\n\nThese statements can be combined with logical operators: not, and, or\n\n(5 < 6) and not (5 == 6)\n\nTrue\n\n\n\nFalse or True\n\nTrue\n\n\n\nTrue or False\n\nTrue\n\n\n\n\n\nUsing strings, we can handle text in Python. These values must be surrounded in quotes — single ('...') is the standard, but double (\"...\") works as well:\n\n'hello'\n\n'hello'\n\n\nWe can also perform operations on strings. For example, we can see how long it is with len():\n\nlen('hello')\n\n5\n\n\nWe can select parts of the string by specifying the index. Note that in Python the 1st character is at index 0:\n\n'hello'[0]\n\n'h'\n\n\nWe can concatentate strings with +:\n\n'hello' + ' ' + 'world'\n\n'hello world'\n\n\nWe can check if characters are in the string with the in operator:\n\n'h' in 'hello'\n\nTrue\n\n\n\n\n\n\nNotice that just typing text causes an error. Errors in Python attempt to clue us in to what went wrong with our code. In this case, we have a NameError exception which tells us that 'hello' is not defined. This means that the Python interpreter looked for a variable named hello, but it didn’t find one.\n\nhello\n\nNameError: name 'hello' is not defined\n\n\nVariables give us a way to store data types. We define a variable using the variable_name = value syntax:\n\nx = 5\ny = 7\nx + y\n\n12\n\n\nThe variable name cannot contain spaces; we usually use _ instead. The best variable names are descriptive ones:\n\nbook_title = 'Hands-On Data Analysis with Pandas'\n\nVariables can be any data type. We can check which one it is with type(), which is a function (more on that later):\n\ntype(x)\n\nint\n\n\n\ntype(book_title)\n\nstr\n\n\nIf we need to see the value of a variable, we can print it using the print() function:\n\nprint(book_title)\n\nHands-On Data Analysis with Pandas\n\n\n\n\n\n\n\nWe can store a collection of items in a list:\n\n['hello', ' ', 'world']\n\n['hello', ' ', 'world']\n\n\nThe list can be stored in a variable. Note that the items in the list can be of different types:\n\nmy_list = ['hello', 3.8, True, 'Python']\ntype(my_list)\n\nlist\n\n\nWe can see how many elements are in the list with len():\n\nlen(my_list)\n\n4\n\n\nWe can also use the in operator to check if a value is in the list:\n\n'world' in my_list\n\nFalse\n\n\nWe can select items in the list just as we did with strings, by providing the index to select:\n\nmy_list[1]\n\n3.8\n\n\nPython also allows us to use negative values, so we can easily select the last one:\n\nmy_list[-1]\n\n'Python'\n\n\nAnother powerful feature of lists (and strings) is slicing. We can grab the middle 2 elements in the list:\n\nmy_list[1:3]\n\n[3.8, True]\n\n\n… or every other one:\n\nmy_list[::2]\n\n['hello', True]\n\n\nWe can even select the list in reverse:\n\nmy_list[::-1]\n\n['Python', True, 3.8, 'hello']\n\n\nNote: This syntax is [start:stop:step] where the selection is inclusive of the start index, but exclusive of the stop index. If start isn’t provided, 0 is used. If stop isn’t provided, the number of elements is used (4, in our case); this works because the stop is exclusive. If step isn’t provided, it is 1.\nWe can use the join() method on a string object to concatenate all the items of a list into single string. The string we call the join() method on will be used as the separator, here we separate with a pipe (|):\n\n'|'.join(['x', 'y', 'z'])\n\n'x|y|z'\n\n\n\n\n\nTuples are similar to lists; however, they can’t be modified after creation i.e. they are immutable. Instead of square brackets, we use parenthesis to create tuples:\n\nmy_tuple = ('a', 5)\ntype(my_tuple)\n\ntuple\n\n\n\nmy_tuple[0]\n\n'a'\n\n\nImmutable objects can’t be modified:\n\nmy_tuple[0] = 'b'\n\nTypeError: 'tuple' object does not support item assignment\n\n\n\n\n\nWe can store mappings of key-value pairs using dictionaries:\n\nshopping_list = {\n 'veggies': ['spinach', 'kale', 'beets'],\n 'fruits': 'bananas',\n 'meat': 0 \n}\ntype(shopping_list)\n\ndict\n\n\nTo access the values associated with a specific key, we use the square bracket notation again:\n\nshopping_list['veggies']\n\n['spinach', 'kale', 'beets']\n\n\nWe can extract all of the keys with keys():\n\nshopping_list.keys()\n\ndict_keys(['veggies', 'fruits', 'meat'])\n\n\nWe can extract all of the values with values():\n\nshopping_list.values()\n\ndict_values([['spinach', 'kale', 'beets'], 'bananas', 0])\n\n\nFinally, we can call items() to get back pairs of (key, value) pairs:\n\nshopping_list.items()\n\ndict_items([('veggies', ['spinach', 'kale', 'beets']), ('fruits', 'bananas'), ('meat', 0)])\n\n\n\n\n\nA set is a collection of unique items; a common use is to remove duplicates from a list. These are written with curly braces also, but notice there is no key-value mapping:\n\nmy_set = {1, 1, 2, 'a'}\ntype(my_set)\n\nset\n\n\nHow many items are in this set?\n\nlen(my_set)\n\n3\n\n\nWe put in 4 items but the set only has 3 because duplicates are removed:\n\nmy_set\n\n{1, 2, 'a'}\n\n\nWe can check if a value is in the set:\n\n2 in my_set\n\nTrue\n\n\n\n\n\n\nWe can define functions to package up our code for reuse. We have already seen some functions: len(), type(), and print(). They are all functions that take arguments. Note that functions don’t need to accept arguments, in which case they are called without passing in anything (e.g. print() versus print(my_string)).\nAside: we can also create lists, sets, dictionaries, and tuples with functions: list(), set(), dict(), and tuple()\n\n\nWe use the def keyword to define functions. Let’s create a function called add() with 2 parameters, x and y, which will be the names the code in the function will use to refer to the arguments we pass in when calling it:\n\ndef add(x, y):\n \"\"\"This is a docstring. It is used to explain how the code works and is optional (but encouraged).\"\"\"\n # this is a comment; it allows us to annotate the code\n print('Performing addition')\n return x + y\n\nOnce we run the code above, our function is ready to use:\n\ntype(add)\n\nfunction\n\n\nLet’s add some numbers:\n\nadd(1, 2)\n\nPerforming addition\n\n\n3\n\n\n\n\n\nWe can store the result in a variable for later:\n\nresult = add(1, 2)\n\nPerforming addition\n\n\nNotice the print statement wasn’t captured in result. This variable will only have what the function returns. This is what the return line in the function definition did:\n\nresult\n\n3\n\n\nNote that functions don’t have to return anything. Consider print():\n\nprint_result = print('hello world')\n\nhello world\n\n\nIf we take a look at what we got back, we see it is a NoneType object:\n\ntype(print_result)\n\nNoneType\n\n\nIn Python, the value None represents null values. We can check if our variable is None:\n\nprint_result is None\n\nTrue\n\n\nWarning: make sure to use comparison operators (e.g. >, >=, <, <=, ==, !=) to compare to values other than None.\n\n\n\nNote that function arguments can be anything, even other functions. We will see several examples of this in the text.\nThe function we defined requires arguments. If we don’t provide them all, it will cause an error:\n\nadd(1)\n\nTypeError: add() missing 1 required positional argument: 'y'\n\n\nWe can use help() to check what arguments the function needs (notice the docstring ends up here):\n\nhelp(add)\n\nHelp on function add in module __main__:\n\nadd(x, y)\n This is a docstring. It is used to explain how the code works and is optional (but encouraged).\n\n\n\nWe will also get errors if we pass in data types that add() can’t work with:\n\nadd(set(), set())\n\nPerforming addition\n\n\nTypeError: unsupported operand type(s) for +: 'set' and 'set'\n\n\nWe will discuss error handling in the text.\n\n\n\n\nSometimes we want to vary the path the code takes based on some criteria. For this we have if, elif, and else. We can use if on its own:\n\ndef make_positive(x):\n \"\"\"Returns a positive x\"\"\"\n if x < 0:\n x *= -1\n return x\n\nCalling this function with negative input causes the code under the if statement to run:\n\nmake_positive(-1)\n\n1\n\n\nCalling this function with positive input skips the code under the if statement, keeping the number positive:\n\nmake_positive(2)\n\n2\n\n\nSometimes we need an else statement as well:\n\ndef add_or_subtract(operation, x, y):\n if operation == 'add':\n return x + y\n else:\n return x - y\n\nThis triggers the code under the if statement:\n\nadd_or_subtract('add', 1, 2)\n\n3\n\n\nSince the Boolean check in the if statement was False, this triggers the code under the else statement:\n\nadd_or_subtract('subtract', 1, 2)\n\n-1\n\n\nFor more complicated logic, we can also use elif. We can have any number of elif statements. Optionally, we can include else.\n\ndef calculate(operation, x, y):\n if operation == 'add':\n return x + y\n elif operation == 'subtract':\n return x - y\n elif operation == 'multiply':\n return x * y\n elif operation == 'division':\n return x / y\n else:\n print(\"This case hasn't been handled\")\n\nThe code keeps checking the conditions in the if statements from top to bottom until it finds multiply:\n\ncalculate('multiply', 3, 4)\n\n12\n\n\nThe code keeps checking the conditions in the if statements from top to bottom until it hits the else statement:\n\ncalculate('power', 3, 4)\n\nThis case hasn't been handled\n\n\n\n\n\n\n\nWith while loops, we can keep running code until some stopping condition is met:\n\ndone = False\nvalue = 2\nwhile not done:\n print('Still going...', value)\n value *= 2\n if value > 10:\n done = True\n\nStill going... 2\nStill going... 4\nStill going... 8\n\n\nNote this can also be written as, by moving the condition to the while statement:\n\nvalue = 2\nwhile value < 10:\n print('Still going...', value)\n value *= 2\n\nStill going... 2\nStill going... 4\nStill going... 8\n\n\n\n\n\nWith for loops, we can run our code for each element in a collection:\n\nfor i in range(5):\n print(i)\n\n0\n1\n2\n3\n4\n\n\nWe can use for loops with lists, tuples, sets, and dictionaries as well:\n\nfor element in my_list:\n print(element)\n\nhello\n3.8\nTrue\nPython\n\n\n\nfor key, value in shopping_list.items():\n print('For', key, 'we need to buy', value)\n\nFor veggies we need to buy ['spinach', 'kale', 'beets']\nFor fruits we need to buy bananas\nFor meat we need to buy 0\n\n\nWith for loops, we don’t have to worry about checking if we have reached the stopping condition. Conversely, while loops can cause infinite loops if we don’t remember to update variables.\n\n\n\n\nWe have been working with the portion of Python that is available without importing additional functionality. The Python standard library that comes with the install of Python is broken up into several modules, but we often only need a few. We can import whatever we need: a module in the standard library, a 3rd-party library, or code that we wrote. This is done with an import statement:\n\nimport math\n\nprint(math.pi)\n\n3.141592653589793\n\n\nIf we only need a small piece from that module, we can do the following instead:\n\nfrom math import pi\n\nprint(pi)\n\n3.141592653589793\n\n\nWarning: anything you import is added to the namespace, so if you create a new variable/function/etc. with the same name it will overwrite the previous value. For this reason, we have to be careful with variable names e.g. if you name something sum, you won’t be able to add using the sum() built-in function anymore. Using notebooks or an IDE will help you avoid these issues with syntax highlighting.\n\n\n\nWe can use pip or conda to install packages, depending on how we created our virtual environment. We will walk through the commands to create virtual environments with conda. The environment MUST be activated before installing the packages for this text; otherwise, it’s possible they interfere with other projects on your machine or vice versa.\nTo install a package, we can use conda install <package_name> to download a package from the default conda channel. Optionally, we can provide a specific version to install conda install pandas==0.23.4. Even further, can define which channel that we install a package from for example we can install a package from the conda-forge channel by with conda install -c conda-forge pandas=0.23.4. Without that specification, we will get the most stable version. When we have many packages to install we will typically use a environment.yml or requirements.txt file: conda env update -f environment.yml from within your active environment or conda env update -n ENVNAME -f environment.yml if you are updating an update you are not actively in.\nNote: running conda env export ENVNAME > environment.yml will send the list of platform-specific packages installed in the activate environment and their respective versions to the environment.yml file.\n\n\n\nSo far we have used Python as a functional programming language, but we also have the option to use it for object-oriented programming. You can think of a class as a way to group similar functionality together. Let’s create a calculator class which can handle mathematical operations for us. For this, we use the class keyword and define methods for taking actions on the calculator. These methods are functions that take self as the first argument. When calling them, we don’t pass in anything for that argument (example after this):\n\nclass Calculator:\n \"\"\"This is the class docstring.\"\"\"\n \n def __init__(self):\n \"\"\"This is a method and it is called when we create an object of type `Calculator`.\"\"\"\n self.on = False\n \n def turn_on(self):\n \"\"\"This method turns on the calculator.\"\"\"\n self.on = True\n \n def add(self, x, y):\n \"\"\"Perform addition if calculator is on\"\"\"\n if self.on:\n return x + y\n else:\n print('the calculator is not on')\n\nIn order to use the calculator, we need to instantiate an instance or object of type Calculator. Since the __init__() method has no parameters other than self, we don’t need to provide anything:\n\nmy_calculator = Calculator()\n\nLet’s try to add some numbers:\n\nmy_calculator.add(1, 2)\n\nthe calculator is not on\n\n\nOops!! The calculator is not on. Let’s turn it on:\n\nmy_calculator.turn_on()\n\nLet’s try again:\n\nmy_calculator.add(1, 2)\n\n3\n\n\nWe can access attributes on object with dot notation. In this example, the only attribute is on, and it is set in the __init__() method:\n\nmy_calculator.on\n\nTrue\n\n\nNote that we can also update attributes:\n\nmy_calculator.on = False\nmy_calculator.add(1, 2)\n\nthe calculator is not on\n\n\nFinally, we can use help() to get more information on the object:\n\nhelp(my_calculator)\n\nHelp on Calculator in module __main__ object:\n\nclass Calculator(builtins.object)\n | This is the class docstring.\n | \n | Methods defined here:\n | \n | __init__(self)\n | This is a method and it is called when we create an object of type `Calculator`.\n | \n | add(self, x, y)\n | Perform addition if calculator is on\n | \n | turn_on(self)\n | This method turns on the calculator.\n | \n | ----------------------------------------------------------------------\n | Data descriptors defined here:\n | \n | __dict__\n | dictionary for instance variables (if defined)\n | \n | __weakref__\n | list of weak references to the object (if defined)\n\n\n\n… and also for a method:\n\nhelp(my_calculator.add)\n\nHelp on method add in module __main__:\n\nadd(x, y) method of __main__.Calculator instance\n Perform addition if calculator is on\n\n\n\n\n\n\nThis was a crash course in Python. This isn’t an exhaustive list of all of the features available to you." + }, + { + "objectID": "notebooks/python_101.html#basic-data-types", + "href": "notebooks/python_101.html#basic-data-types", + "title": "Python 101", + "section": "", + "text": "Numbers in Python can be represented as integers (e.g. 5) or floats (e.g. 5.0). We can perform operations on them:\n\n5 + 6\n\n11\n\n\n\n2.5 / 3\n\n0.8333333333333334\n\n\n\n\n\nWe can check for equality giving us a Boolean:\n\n5 == 6\n\nFalse\n\n\n\n5 < 6\n\nTrue\n\n\nThese statements can be combined with logical operators: not, and, or\n\n(5 < 6) and not (5 == 6)\n\nTrue\n\n\n\nFalse or True\n\nTrue\n\n\n\nTrue or False\n\nTrue\n\n\n\n\n\nUsing strings, we can handle text in Python. These values must be surrounded in quotes — single ('...') is the standard, but double (\"...\") works as well:\n\n'hello'\n\n'hello'\n\n\nWe can also perform operations on strings. For example, we can see how long it is with len():\n\nlen('hello')\n\n5\n\n\nWe can select parts of the string by specifying the index. Note that in Python the 1st character is at index 0:\n\n'hello'[0]\n\n'h'\n\n\nWe can concatentate strings with +:\n\n'hello' + ' ' + 'world'\n\n'hello world'\n\n\nWe can check if characters are in the string with the in operator:\n\n'h' in 'hello'\n\nTrue" + }, + { + "objectID": "notebooks/python_101.html#variables", + "href": "notebooks/python_101.html#variables", + "title": "Python 101", + "section": "", + "text": "Notice that just typing text causes an error. Errors in Python attempt to clue us in to what went wrong with our code. In this case, we have a NameError exception which tells us that 'hello' is not defined. This means that the Python interpreter looked for a variable named hello, but it didn’t find one.\n\nhello\n\nNameError: name 'hello' is not defined\n\n\nVariables give us a way to store data types. We define a variable using the variable_name = value syntax:\n\nx = 5\ny = 7\nx + y\n\n12\n\n\nThe variable name cannot contain spaces; we usually use _ instead. The best variable names are descriptive ones:\n\nbook_title = 'Hands-On Data Analysis with Pandas'\n\nVariables can be any data type. We can check which one it is with type(), which is a function (more on that later):\n\ntype(x)\n\nint\n\n\n\ntype(book_title)\n\nstr\n\n\nIf we need to see the value of a variable, we can print it using the print() function:\n\nprint(book_title)\n\nHands-On Data Analysis with Pandas" + }, + { + "objectID": "notebooks/python_101.html#collections-of-items", + "href": "notebooks/python_101.html#collections-of-items", + "title": "Python 101", + "section": "", + "text": "We can store a collection of items in a list:\n\n['hello', ' ', 'world']\n\n['hello', ' ', 'world']\n\n\nThe list can be stored in a variable. Note that the items in the list can be of different types:\n\nmy_list = ['hello', 3.8, True, 'Python']\ntype(my_list)\n\nlist\n\n\nWe can see how many elements are in the list with len():\n\nlen(my_list)\n\n4\n\n\nWe can also use the in operator to check if a value is in the list:\n\n'world' in my_list\n\nFalse\n\n\nWe can select items in the list just as we did with strings, by providing the index to select:\n\nmy_list[1]\n\n3.8\n\n\nPython also allows us to use negative values, so we can easily select the last one:\n\nmy_list[-1]\n\n'Python'\n\n\nAnother powerful feature of lists (and strings) is slicing. We can grab the middle 2 elements in the list:\n\nmy_list[1:3]\n\n[3.8, True]\n\n\n… or every other one:\n\nmy_list[::2]\n\n['hello', True]\n\n\nWe can even select the list in reverse:\n\nmy_list[::-1]\n\n['Python', True, 3.8, 'hello']\n\n\nNote: This syntax is [start:stop:step] where the selection is inclusive of the start index, but exclusive of the stop index. If start isn’t provided, 0 is used. If stop isn’t provided, the number of elements is used (4, in our case); this works because the stop is exclusive. If step isn’t provided, it is 1.\nWe can use the join() method on a string object to concatenate all the items of a list into single string. The string we call the join() method on will be used as the separator, here we separate with a pipe (|):\n\n'|'.join(['x', 'y', 'z'])\n\n'x|y|z'\n\n\n\n\n\nTuples are similar to lists; however, they can’t be modified after creation i.e. they are immutable. Instead of square brackets, we use parenthesis to create tuples:\n\nmy_tuple = ('a', 5)\ntype(my_tuple)\n\ntuple\n\n\n\nmy_tuple[0]\n\n'a'\n\n\nImmutable objects can’t be modified:\n\nmy_tuple[0] = 'b'\n\nTypeError: 'tuple' object does not support item assignment\n\n\n\n\n\nWe can store mappings of key-value pairs using dictionaries:\n\nshopping_list = {\n 'veggies': ['spinach', 'kale', 'beets'],\n 'fruits': 'bananas',\n 'meat': 0 \n}\ntype(shopping_list)\n\ndict\n\n\nTo access the values associated with a specific key, we use the square bracket notation again:\n\nshopping_list['veggies']\n\n['spinach', 'kale', 'beets']\n\n\nWe can extract all of the keys with keys():\n\nshopping_list.keys()\n\ndict_keys(['veggies', 'fruits', 'meat'])\n\n\nWe can extract all of the values with values():\n\nshopping_list.values()\n\ndict_values([['spinach', 'kale', 'beets'], 'bananas', 0])\n\n\nFinally, we can call items() to get back pairs of (key, value) pairs:\n\nshopping_list.items()\n\ndict_items([('veggies', ['spinach', 'kale', 'beets']), ('fruits', 'bananas'), ('meat', 0)])\n\n\n\n\n\nA set is a collection of unique items; a common use is to remove duplicates from a list. These are written with curly braces also, but notice there is no key-value mapping:\n\nmy_set = {1, 1, 2, 'a'}\ntype(my_set)\n\nset\n\n\nHow many items are in this set?\n\nlen(my_set)\n\n3\n\n\nWe put in 4 items but the set only has 3 because duplicates are removed:\n\nmy_set\n\n{1, 2, 'a'}\n\n\nWe can check if a value is in the set:\n\n2 in my_set\n\nTrue" + }, + { + "objectID": "notebooks/python_101.html#functions", + "href": "notebooks/python_101.html#functions", + "title": "Python 101", + "section": "", + "text": "We can define functions to package up our code for reuse. We have already seen some functions: len(), type(), and print(). They are all functions that take arguments. Note that functions don’t need to accept arguments, in which case they are called without passing in anything (e.g. print() versus print(my_string)).\nAside: we can also create lists, sets, dictionaries, and tuples with functions: list(), set(), dict(), and tuple()\n\n\nWe use the def keyword to define functions. Let’s create a function called add() with 2 parameters, x and y, which will be the names the code in the function will use to refer to the arguments we pass in when calling it:\n\ndef add(x, y):\n \"\"\"This is a docstring. It is used to explain how the code works and is optional (but encouraged).\"\"\"\n # this is a comment; it allows us to annotate the code\n print('Performing addition')\n return x + y\n\nOnce we run the code above, our function is ready to use:\n\ntype(add)\n\nfunction\n\n\nLet’s add some numbers:\n\nadd(1, 2)\n\nPerforming addition\n\n\n3\n\n\n\n\n\nWe can store the result in a variable for later:\n\nresult = add(1, 2)\n\nPerforming addition\n\n\nNotice the print statement wasn’t captured in result. This variable will only have what the function returns. This is what the return line in the function definition did:\n\nresult\n\n3\n\n\nNote that functions don’t have to return anything. Consider print():\n\nprint_result = print('hello world')\n\nhello world\n\n\nIf we take a look at what we got back, we see it is a NoneType object:\n\ntype(print_result)\n\nNoneType\n\n\nIn Python, the value None represents null values. We can check if our variable is None:\n\nprint_result is None\n\nTrue\n\n\nWarning: make sure to use comparison operators (e.g. >, >=, <, <=, ==, !=) to compare to values other than None.\n\n\n\nNote that function arguments can be anything, even other functions. We will see several examples of this in the text.\nThe function we defined requires arguments. If we don’t provide them all, it will cause an error:\n\nadd(1)\n\nTypeError: add() missing 1 required positional argument: 'y'\n\n\nWe can use help() to check what arguments the function needs (notice the docstring ends up here):\n\nhelp(add)\n\nHelp on function add in module __main__:\n\nadd(x, y)\n This is a docstring. It is used to explain how the code works and is optional (but encouraged).\n\n\n\nWe will also get errors if we pass in data types that add() can’t work with:\n\nadd(set(), set())\n\nPerforming addition\n\n\nTypeError: unsupported operand type(s) for +: 'set' and 'set'\n\n\nWe will discuss error handling in the text." + }, + { + "objectID": "notebooks/python_101.html#control-flow-statements", + "href": "notebooks/python_101.html#control-flow-statements", + "title": "Python 101", + "section": "", + "text": "Sometimes we want to vary the path the code takes based on some criteria. For this we have if, elif, and else. We can use if on its own:\n\ndef make_positive(x):\n \"\"\"Returns a positive x\"\"\"\n if x < 0:\n x *= -1\n return x\n\nCalling this function with negative input causes the code under the if statement to run:\n\nmake_positive(-1)\n\n1\n\n\nCalling this function with positive input skips the code under the if statement, keeping the number positive:\n\nmake_positive(2)\n\n2\n\n\nSometimes we need an else statement as well:\n\ndef add_or_subtract(operation, x, y):\n if operation == 'add':\n return x + y\n else:\n return x - y\n\nThis triggers the code under the if statement:\n\nadd_or_subtract('add', 1, 2)\n\n3\n\n\nSince the Boolean check in the if statement was False, this triggers the code under the else statement:\n\nadd_or_subtract('subtract', 1, 2)\n\n-1\n\n\nFor more complicated logic, we can also use elif. We can have any number of elif statements. Optionally, we can include else.\n\ndef calculate(operation, x, y):\n if operation == 'add':\n return x + y\n elif operation == 'subtract':\n return x - y\n elif operation == 'multiply':\n return x * y\n elif operation == 'division':\n return x / y\n else:\n print(\"This case hasn't been handled\")\n\nThe code keeps checking the conditions in the if statements from top to bottom until it finds multiply:\n\ncalculate('multiply', 3, 4)\n\n12\n\n\nThe code keeps checking the conditions in the if statements from top to bottom until it hits the else statement:\n\ncalculate('power', 3, 4)\n\nThis case hasn't been handled" + }, + { + "objectID": "notebooks/python_101.html#loops", + "href": "notebooks/python_101.html#loops", + "title": "Python 101", + "section": "", + "text": "With while loops, we can keep running code until some stopping condition is met:\n\ndone = False\nvalue = 2\nwhile not done:\n print('Still going...', value)\n value *= 2\n if value > 10:\n done = True\n\nStill going... 2\nStill going... 4\nStill going... 8\n\n\nNote this can also be written as, by moving the condition to the while statement:\n\nvalue = 2\nwhile value < 10:\n print('Still going...', value)\n value *= 2\n\nStill going... 2\nStill going... 4\nStill going... 8\n\n\n\n\n\nWith for loops, we can run our code for each element in a collection:\n\nfor i in range(5):\n print(i)\n\n0\n1\n2\n3\n4\n\n\nWe can use for loops with lists, tuples, sets, and dictionaries as well:\n\nfor element in my_list:\n print(element)\n\nhello\n3.8\nTrue\nPython\n\n\n\nfor key, value in shopping_list.items():\n print('For', key, 'we need to buy', value)\n\nFor veggies we need to buy ['spinach', 'kale', 'beets']\nFor fruits we need to buy bananas\nFor meat we need to buy 0\n\n\nWith for loops, we don’t have to worry about checking if we have reached the stopping condition. Conversely, while loops can cause infinite loops if we don’t remember to update variables." + }, + { + "objectID": "notebooks/python_101.html#imports", + "href": "notebooks/python_101.html#imports", + "title": "Python 101", + "section": "", + "text": "We have been working with the portion of Python that is available without importing additional functionality. The Python standard library that comes with the install of Python is broken up into several modules, but we often only need a few. We can import whatever we need: a module in the standard library, a 3rd-party library, or code that we wrote. This is done with an import statement:\n\nimport math\n\nprint(math.pi)\n\n3.141592653589793\n\n\nIf we only need a small piece from that module, we can do the following instead:\n\nfrom math import pi\n\nprint(pi)\n\n3.141592653589793\n\n\nWarning: anything you import is added to the namespace, so if you create a new variable/function/etc. with the same name it will overwrite the previous value. For this reason, we have to be careful with variable names e.g. if you name something sum, you won’t be able to add using the sum() built-in function anymore. Using notebooks or an IDE will help you avoid these issues with syntax highlighting." + }, + { + "objectID": "notebooks/python_101.html#installing-3rd-party-packages", + "href": "notebooks/python_101.html#installing-3rd-party-packages", + "title": "Python 101", + "section": "", + "text": "We can use pip or conda to install packages, depending on how we created our virtual environment. We will walk through the commands to create virtual environments with conda. The environment MUST be activated before installing the packages for this text; otherwise, it’s possible they interfere with other projects on your machine or vice versa.\nTo install a package, we can use conda install <package_name> to download a package from the default conda channel. Optionally, we can provide a specific version to install conda install pandas==0.23.4. Even further, can define which channel that we install a package from for example we can install a package from the conda-forge channel by with conda install -c conda-forge pandas=0.23.4. Without that specification, we will get the most stable version. When we have many packages to install we will typically use a environment.yml or requirements.txt file: conda env update -f environment.yml from within your active environment or conda env update -n ENVNAME -f environment.yml if you are updating an update you are not actively in.\nNote: running conda env export ENVNAME > environment.yml will send the list of platform-specific packages installed in the activate environment and their respective versions to the environment.yml file." + }, + { + "objectID": "notebooks/python_101.html#classes", + "href": "notebooks/python_101.html#classes", + "title": "Python 101", + "section": "", + "text": "So far we have used Python as a functional programming language, but we also have the option to use it for object-oriented programming. You can think of a class as a way to group similar functionality together. Let’s create a calculator class which can handle mathematical operations for us. For this, we use the class keyword and define methods for taking actions on the calculator. These methods are functions that take self as the first argument. When calling them, we don’t pass in anything for that argument (example after this):\n\nclass Calculator:\n \"\"\"This is the class docstring.\"\"\"\n \n def __init__(self):\n \"\"\"This is a method and it is called when we create an object of type `Calculator`.\"\"\"\n self.on = False\n \n def turn_on(self):\n \"\"\"This method turns on the calculator.\"\"\"\n self.on = True\n \n def add(self, x, y):\n \"\"\"Perform addition if calculator is on\"\"\"\n if self.on:\n return x + y\n else:\n print('the calculator is not on')\n\nIn order to use the calculator, we need to instantiate an instance or object of type Calculator. Since the __init__() method has no parameters other than self, we don’t need to provide anything:\n\nmy_calculator = Calculator()\n\nLet’s try to add some numbers:\n\nmy_calculator.add(1, 2)\n\nthe calculator is not on\n\n\nOops!! The calculator is not on. Let’s turn it on:\n\nmy_calculator.turn_on()\n\nLet’s try again:\n\nmy_calculator.add(1, 2)\n\n3\n\n\nWe can access attributes on object with dot notation. In this example, the only attribute is on, and it is set in the __init__() method:\n\nmy_calculator.on\n\nTrue\n\n\nNote that we can also update attributes:\n\nmy_calculator.on = False\nmy_calculator.add(1, 2)\n\nthe calculator is not on\n\n\nFinally, we can use help() to get more information on the object:\n\nhelp(my_calculator)\n\nHelp on Calculator in module __main__ object:\n\nclass Calculator(builtins.object)\n | This is the class docstring.\n | \n | Methods defined here:\n | \n | __init__(self)\n | This is a method and it is called when we create an object of type `Calculator`.\n | \n | add(self, x, y)\n | Perform addition if calculator is on\n | \n | turn_on(self)\n | This method turns on the calculator.\n | \n | ----------------------------------------------------------------------\n | Data descriptors defined here:\n | \n | __dict__\n | dictionary for instance variables (if defined)\n | \n | __weakref__\n | list of weak references to the object (if defined)\n\n\n\n… and also for a method:\n\nhelp(my_calculator.add)\n\nHelp on method add in module __main__:\n\nadd(x, y) method of __main__.Calculator instance\n Perform addition if calculator is on" + }, + { + "objectID": "notebooks/python_101.html#next-steps", + "href": "notebooks/python_101.html#next-steps", + "title": "Python 101", + "section": "", + "text": "This was a crash course in Python. This isn’t an exhaustive list of all of the features available to you." + }, + { + "objectID": "notebooks/reading_local_files.html", + "href": "notebooks/reading_local_files.html", + "title": "An Introduction to Jupyter Notebooks", + "section": "", + "text": "Jupyter Notebooks are a file format (*.ipynb) that you can execute and explain your code in a step-wise format. > Jupyter Notebooks supports not only code execution in Python, but over 40 languages including R, Lua, Rust, and Julia with numerous kernels.\nWe can write in Markdown to write text with some level of control over your formatting. - Here’s a Link to Basic Markdown - Here’s a link to Markdown’s Extended Syntax\nTopics We Will Cover - Importing different files and filetypes with pandas - Basic Statistical Analysis of tabular data with pandas and numpy - Creating Charts with python packages from the Matplotlib, Plotly, or HoloViz Ecosystem - Evaluate the potential usecases for each visualization package\n This is you, enjoying the learning process.\nStep 1: Import pandas into your python program.\n\nimport pandas as pd\n\n# This will import the pandas and numpy packages into your Python program.\n\ndf_json = pd.read_json('../data/food-waste-pilot/food-waste-pilot.json')\ndf_csv = pd.read_csv('../data/food-waste-pilot/food-waste-pilot.csv')\ndf_xlsx = pd.read_excel('../data/food-waste-pilot/food-waste-pilot.xlsx')\n\n\ndf_csv.shape\n\n(152, 3)\n\n\n\ndf_csv.head() # Grabs the top 5 items in your Dataframe by default.\n\n\n\n\n\n\n\n\nCollection Date\nFood Waste Collected\nEstimated Earned Compost Created\n\n\n\n\n0\n2022-02-25\n250.8\n25\n\n\n1\n2022-03-02\n298.8\n30\n\n\n2\n2022-03-21\n601.2\n60\n\n\n3\n2022-03-28\n857.2\n86\n\n\n4\n2022-03-30\n610.8\n61\n\n\n\n\n\n\n\n\ndf_csv.tail() # Grabs the bottom 5 items in your Dataframe by default.\n\n\n\n\n\n\n\n\nCollection Date\nFood Waste Collected\nEstimated Earned Compost Created\n\n\n\n\n147\n2022-10-12\n385.8\n39\n\n\n148\n2022-10-28\n713.6\n71\n\n\n149\n2022-10-31\n953.4\n95\n\n\n150\n2022-12-14\n694.4\n69\n\n\n151\n2023-01-06\n968.6\n97\n\n\n\n\n\n\n\n\ndf_csv.columns\n\nIndex(['Collection Date', 'Food Waste Collected',\n 'Estimated Earned Compost Created'],\n dtype='object')\n\n\n\ndf_csv.dtypes # Returns the data types of your columns.\n\nCollection Date object\nFood Waste Collected float64\nEstimated Earned Compost Created int64\ndtype: object\n\n\n\ndf_csv.describe()\n\n\n\n\n\n\n\n\nFood Waste Collected\nEstimated Earned Compost Created\n\n\n\n\ncount\n152.000000\n152.000000\n\n\nmean\n526.873684\n52.611842\n\n\nstd\n197.838075\n19.787631\n\n\nmin\n0.000000\n0.000000\n\n\n25%\n398.050000\n39.750000\n\n\n50%\n531.500000\n53.000000\n\n\n75%\n658.900000\n66.000000\n\n\nmax\n1065.800000\n107.000000\n\n\n\n\n\n\n\n\ndf_csv.info() # Returns index, column names, a count of Non-Null values, and data types.\n\n<class 'pandas.core.frame.DataFrame'>\nRangeIndex: 152 entries, 0 to 151\nData columns (total 3 columns):\n # Column Non-Null Count Dtype \n--- ------ -------------- ----- \n 0 Collection Date 152 non-null object \n 1 Food Waste Collected 152 non-null float64\n 2 Estimated Earned Compost Created 152 non-null int64 \ndtypes: float64(1), int64(1), object(1)\nmemory usage: 3.7+ KB\n\n\nThere are multiple methods to do type conversion using pandas as well.\n\n# Oh no, we can see that our Collection Date is not the data type that we want, we need to convert it to a date value.\n\ndf_csv['Collection Date'] = pd.to_datetime(df_csv['Collection Date'])\n\n\ndf_csv.info()\n\n<class 'pandas.core.frame.DataFrame'>\nRangeIndex: 152 entries, 0 to 151\nData columns (total 3 columns):\n # Column Non-Null Count Dtype \n--- ------ -------------- ----- \n 0 Collection Date 152 non-null datetime64[ns]\n 1 Food Waste Collected 152 non-null float64 \n 2 Estimated Earned Compost Created 152 non-null int64 \ndtypes: datetime64[ns](1), float64(1), int64(1)\nmemory usage: 3.7 KB\n\n\n\n# An alternative way to do this date conversion:\n\ndf_csv['Collection Date'] = df_csv['Collection Date'].apply(pd.to_datetime)\n\n\n# astype() is more generic method to convert data types\n\ndf_csv['Collection Date'] = df_csv['Collection Date'].astype('datetime64[ns]')\n\n\ndf_csv.dtypes\n\nCollection Date datetime64[ns]\nFood Waste Collected float64\nEstimated Earned Compost Created int64\ndtype: object\n\n\n\n# Now that we have converted our Collection Date column to a datetime data type, we can use the dt.day_name() method to create a new column that contains the day of the week.\n\ndf_csv['Day of Week'] = df_csv['Collection Date'].dt.day_name()\n\n\n# What if we want to know the date that we collected the most food waste?\n\ndf_csv.loc[\n df_csv['Food Waste Collected'].idxmax(),\n ['Collection Date']\n]\n\nCollection Date 2022-08-10 00:00:00\nName: 20, dtype: object\n\n\n\n# If you wanted to see our top 10 collection dates, you could do this:\n\ndf_csv.nlargest(10,'Food Waste Collected')\n\n\n\n\n\n\n\n\nCollection Date\nFood Waste Collected\nEstimated Earned Compost Created\nDay of Week\n\n\n\n\n20\n2022-08-10\n1065.8\n107\nWednesday\n\n\n124\n2022-12-27\n987.4\n99\nTuesday\n\n\n48\n2022-09-12\n977.8\n98\nMonday\n\n\n151\n2023-01-06\n968.6\n97\nFriday\n\n\n149\n2022-10-31\n953.4\n95\nMonday\n\n\n3\n2022-03-28\n857.2\n86\nMonday\n\n\n123\n2022-11-28\n844.4\n84\nMonday\n\n\n57\n2022-12-05\n834.4\n83\nMonday\n\n\n91\n2022-12-30\n815.4\n82\nFriday\n\n\n137\n2022-07-18\n807.8\n81\nMonday\n\n\n\n\n\n\n\n\ndf_csv.nsmallest(10,'Food Waste Collected')\n\n\n\n\n\n\n\n\nCollection Date\nFood Waste Collected\nEstimated Earned Compost Created\nDay of Week\n\n\n\n\n53\n2022-11-11\n0.0\n0\nFriday\n\n\n95\n2023-01-16\n0.0\n0\nMonday\n\n\n114\n2022-09-05\n0.0\n0\nMonday\n\n\n97\n2022-02-09\n59.0\n6\nWednesday\n\n\n36\n2022-02-16\n102.8\n10\nWednesday\n\n\n63\n2022-02-21\n183.8\n18\nMonday\n\n\n61\n2022-02-11\n197.0\n20\nFriday\n\n\n39\n2022-04-15\n200.8\n20\nFriday\n\n\n62\n2022-02-18\n202.8\n20\nFriday\n\n\n29\n2022-12-10\n205.8\n21\nSaturday\n\n\n\n\n\n\n\n\ndf_csv.plot()\n\n\n\n\n\n\n\n\n\n\n\ndf_csv_parsed_dates = pd.read_csv('../data/food-waste-pilot/food-waste-pilot.csv', parse_dates=True, index_col=\"Collection Date\")\n\n\ndf_csv_parsed_dates.plot()" + }, + { + "objectID": "notebooks/reading_local_files.html#you-have-to-make-sure-that-pandas-parses-your-dates", + "href": "notebooks/reading_local_files.html#you-have-to-make-sure-that-pandas-parses-your-dates", + "title": "An Introduction to Jupyter Notebooks", + "section": "", + "text": "df_csv_parsed_dates = pd.read_csv('../data/food-waste-pilot/food-waste-pilot.csv', parse_dates=True, index_col=\"Collection Date\")\n\n\ndf_csv_parsed_dates.plot()" + } +] \ No newline at end of file diff --git a/docs/site_libs/bootstrap/bootstrap-icons.css b/docs/site_libs/bootstrap/bootstrap-icons.css new file mode 100644 index 0000000..285e444 --- /dev/null +++ b/docs/site_libs/bootstrap/bootstrap-icons.css @@ -0,0 +1,2078 @@ +/*! + * Bootstrap Icons v1.11.1 (https://icons.getbootstrap.com/) + * Copyright 2019-2023 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE) + */ + +@font-face { + font-display: block; + font-family: "bootstrap-icons"; + src: +url("./bootstrap-icons.woff?2820a3852bdb9a5832199cc61cec4e65") 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-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-fill::before { content: "\f68b"; } +.bi-envelope-check::before { content: "\f68c"; } +.bi-envelope-dash-fill::before { content: "\f68e"; } +.bi-envelope-dash::before { content: "\f68f"; } +.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-fill::before { content: "\f696"; } +.bi-envelope-slash::before { content: "\f697"; } +.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-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-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::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::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-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::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::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"; } +.bi-1-circle-fill::before { content: "\f796"; } +.bi-1-circle::before { content: "\f797"; } +.bi-1-square-fill::before { content: "\f798"; } +.bi-1-square::before { content: "\f799"; } +.bi-2-circle-fill::before { content: "\f79c"; } +.bi-2-circle::before { content: "\f79d"; } +.bi-2-square-fill::before { content: "\f79e"; } +.bi-2-square::before { content: "\f79f"; } +.bi-3-circle-fill::before { content: "\f7a2"; } +.bi-3-circle::before { content: "\f7a3"; } +.bi-3-square-fill::before { content: "\f7a4"; } +.bi-3-square::before { content: "\f7a5"; } +.bi-4-circle-fill::before { content: "\f7a8"; } +.bi-4-circle::before { content: "\f7a9"; } +.bi-4-square-fill::before { content: "\f7aa"; } +.bi-4-square::before { content: "\f7ab"; } +.bi-5-circle-fill::before { content: "\f7ae"; } +.bi-5-circle::before { content: "\f7af"; } +.bi-5-square-fill::before { content: "\f7b0"; } +.bi-5-square::before { content: "\f7b1"; } +.bi-6-circle-fill::before { content: "\f7b4"; } +.bi-6-circle::before { content: "\f7b5"; } +.bi-6-square-fill::before { content: "\f7b6"; } +.bi-6-square::before { content: "\f7b7"; } +.bi-7-circle-fill::before { content: "\f7ba"; } +.bi-7-circle::before { content: "\f7bb"; } +.bi-7-square-fill::before { content: "\f7bc"; } +.bi-7-square::before { content: "\f7bd"; } +.bi-8-circle-fill::before { content: "\f7c0"; } +.bi-8-circle::before { content: "\f7c1"; } +.bi-8-square-fill::before { content: "\f7c2"; } +.bi-8-square::before { content: "\f7c3"; } +.bi-9-circle-fill::before { content: "\f7c6"; } +.bi-9-circle::before { content: "\f7c7"; } +.bi-9-square-fill::before { content: "\f7c8"; } +.bi-9-square::before { content: "\f7c9"; } +.bi-airplane-engines-fill::before { content: "\f7ca"; } +.bi-airplane-engines::before { content: "\f7cb"; } +.bi-airplane-fill::before { content: "\f7cc"; } +.bi-airplane::before { content: "\f7cd"; } +.bi-alexa::before { content: "\f7ce"; } +.bi-alipay::before { content: "\f7cf"; } +.bi-android::before { content: "\f7d0"; } +.bi-android2::before { content: "\f7d1"; } +.bi-box-fill::before { content: "\f7d2"; } +.bi-box-seam-fill::before { content: "\f7d3"; } +.bi-browser-chrome::before { content: "\f7d4"; } +.bi-browser-edge::before { content: "\f7d5"; } +.bi-browser-firefox::before { content: "\f7d6"; } +.bi-browser-safari::before { content: "\f7d7"; } +.bi-c-circle-fill::before { content: "\f7da"; } +.bi-c-circle::before { content: "\f7db"; } +.bi-c-square-fill::before { content: "\f7dc"; } +.bi-c-square::before { content: "\f7dd"; } +.bi-capsule-pill::before { content: "\f7de"; } +.bi-capsule::before { content: "\f7df"; } +.bi-car-front-fill::before { content: "\f7e0"; } +.bi-car-front::before { content: "\f7e1"; } +.bi-cassette-fill::before { content: "\f7e2"; } +.bi-cassette::before { content: "\f7e3"; } +.bi-cc-circle-fill::before { content: "\f7e6"; } +.bi-cc-circle::before { content: "\f7e7"; } +.bi-cc-square-fill::before { content: "\f7e8"; } +.bi-cc-square::before { content: "\f7e9"; } +.bi-cup-hot-fill::before { content: "\f7ea"; } +.bi-cup-hot::before { content: "\f7eb"; } +.bi-currency-rupee::before { content: "\f7ec"; } +.bi-dropbox::before { content: "\f7ed"; } +.bi-escape::before { content: "\f7ee"; } +.bi-fast-forward-btn-fill::before { content: "\f7ef"; } +.bi-fast-forward-btn::before { content: "\f7f0"; } +.bi-fast-forward-circle-fill::before { content: "\f7f1"; } +.bi-fast-forward-circle::before { content: "\f7f2"; } +.bi-fast-forward-fill::before { content: "\f7f3"; } +.bi-fast-forward::before { content: "\f7f4"; } +.bi-filetype-sql::before { content: "\f7f5"; } +.bi-fire::before { content: "\f7f6"; } +.bi-google-play::before { content: "\f7f7"; } +.bi-h-circle-fill::before { content: "\f7fa"; } +.bi-h-circle::before { content: "\f7fb"; } +.bi-h-square-fill::before { content: "\f7fc"; } +.bi-h-square::before { content: "\f7fd"; } +.bi-indent::before { content: "\f7fe"; } +.bi-lungs-fill::before { content: "\f7ff"; } +.bi-lungs::before { content: "\f800"; } +.bi-microsoft-teams::before { content: "\f801"; } +.bi-p-circle-fill::before { content: "\f804"; } +.bi-p-circle::before { content: "\f805"; } +.bi-p-square-fill::before { content: "\f806"; } +.bi-p-square::before { content: "\f807"; } +.bi-pass-fill::before { content: "\f808"; } +.bi-pass::before { content: "\f809"; } +.bi-prescription::before { content: "\f80a"; } +.bi-prescription2::before { content: "\f80b"; } +.bi-r-circle-fill::before { content: "\f80e"; } +.bi-r-circle::before { content: "\f80f"; } +.bi-r-square-fill::before { content: "\f810"; } +.bi-r-square::before { content: "\f811"; } +.bi-repeat-1::before { content: "\f812"; } +.bi-repeat::before { content: "\f813"; } +.bi-rewind-btn-fill::before { content: "\f814"; } +.bi-rewind-btn::before { content: "\f815"; } +.bi-rewind-circle-fill::before { content: "\f816"; } +.bi-rewind-circle::before { content: "\f817"; } +.bi-rewind-fill::before { content: "\f818"; } +.bi-rewind::before { content: "\f819"; } +.bi-train-freight-front-fill::before { content: "\f81a"; } +.bi-train-freight-front::before { content: "\f81b"; } +.bi-train-front-fill::before { content: "\f81c"; } +.bi-train-front::before { content: "\f81d"; } +.bi-train-lightrail-front-fill::before { content: "\f81e"; } +.bi-train-lightrail-front::before { content: "\f81f"; } +.bi-truck-front-fill::before { content: "\f820"; } +.bi-truck-front::before { content: "\f821"; } +.bi-ubuntu::before { content: "\f822"; } +.bi-unindent::before { content: "\f823"; } +.bi-unity::before { content: "\f824"; } +.bi-universal-access-circle::before { content: "\f825"; } +.bi-universal-access::before { content: "\f826"; } +.bi-virus::before { content: "\f827"; } +.bi-virus2::before { content: "\f828"; } +.bi-wechat::before { content: "\f829"; } +.bi-yelp::before { content: "\f82a"; } +.bi-sign-stop-fill::before { content: "\f82b"; } +.bi-sign-stop-lights-fill::before { content: "\f82c"; } +.bi-sign-stop-lights::before { content: "\f82d"; } +.bi-sign-stop::before { content: "\f82e"; } +.bi-sign-turn-left-fill::before { content: "\f82f"; } +.bi-sign-turn-left::before { content: "\f830"; } +.bi-sign-turn-right-fill::before { content: "\f831"; } +.bi-sign-turn-right::before { content: "\f832"; } +.bi-sign-turn-slight-left-fill::before { content: "\f833"; } +.bi-sign-turn-slight-left::before { content: "\f834"; } +.bi-sign-turn-slight-right-fill::before { content: "\f835"; } +.bi-sign-turn-slight-right::before { content: "\f836"; } +.bi-sign-yield-fill::before { content: "\f837"; } +.bi-sign-yield::before { content: "\f838"; } +.bi-ev-station-fill::before { content: "\f839"; } +.bi-ev-station::before { content: "\f83a"; } +.bi-fuel-pump-diesel-fill::before { content: "\f83b"; } +.bi-fuel-pump-diesel::before { content: "\f83c"; } +.bi-fuel-pump-fill::before { content: "\f83d"; } +.bi-fuel-pump::before { content: "\f83e"; } +.bi-0-circle-fill::before { content: "\f83f"; } +.bi-0-circle::before { content: "\f840"; } +.bi-0-square-fill::before { content: "\f841"; } +.bi-0-square::before { content: "\f842"; } +.bi-rocket-fill::before { content: "\f843"; } +.bi-rocket-takeoff-fill::before { content: "\f844"; } +.bi-rocket-takeoff::before { content: "\f845"; } +.bi-rocket::before { content: "\f846"; } +.bi-stripe::before { content: "\f847"; } +.bi-subscript::before { content: "\f848"; } +.bi-superscript::before { content: "\f849"; } +.bi-trello::before { content: "\f84a"; } +.bi-envelope-at-fill::before { content: "\f84b"; } +.bi-envelope-at::before { content: "\f84c"; } +.bi-regex::before { content: "\f84d"; } +.bi-text-wrap::before { content: "\f84e"; } +.bi-sign-dead-end-fill::before { content: "\f84f"; } +.bi-sign-dead-end::before { content: "\f850"; } +.bi-sign-do-not-enter-fill::before { content: "\f851"; } +.bi-sign-do-not-enter::before { content: "\f852"; } +.bi-sign-intersection-fill::before { content: "\f853"; } +.bi-sign-intersection-side-fill::before { content: "\f854"; } +.bi-sign-intersection-side::before { content: "\f855"; } +.bi-sign-intersection-t-fill::before { content: "\f856"; } +.bi-sign-intersection-t::before { content: "\f857"; } +.bi-sign-intersection-y-fill::before { content: "\f858"; } +.bi-sign-intersection-y::before { content: "\f859"; } +.bi-sign-intersection::before { content: "\f85a"; } +.bi-sign-merge-left-fill::before { content: "\f85b"; } +.bi-sign-merge-left::before { content: "\f85c"; } +.bi-sign-merge-right-fill::before { content: "\f85d"; } +.bi-sign-merge-right::before { content: "\f85e"; } +.bi-sign-no-left-turn-fill::before { content: "\f85f"; } +.bi-sign-no-left-turn::before { content: "\f860"; } +.bi-sign-no-parking-fill::before { content: "\f861"; } +.bi-sign-no-parking::before { content: "\f862"; } +.bi-sign-no-right-turn-fill::before { content: "\f863"; } +.bi-sign-no-right-turn::before { content: "\f864"; } +.bi-sign-railroad-fill::before { content: "\f865"; } +.bi-sign-railroad::before { content: "\f866"; } +.bi-building-add::before { content: "\f867"; } +.bi-building-check::before { content: "\f868"; } +.bi-building-dash::before { content: "\f869"; } +.bi-building-down::before { content: "\f86a"; } +.bi-building-exclamation::before { content: "\f86b"; } +.bi-building-fill-add::before { content: "\f86c"; } +.bi-building-fill-check::before { content: "\f86d"; } +.bi-building-fill-dash::before { content: "\f86e"; } +.bi-building-fill-down::before { content: "\f86f"; } +.bi-building-fill-exclamation::before { content: "\f870"; } +.bi-building-fill-gear::before { content: "\f871"; } +.bi-building-fill-lock::before { content: "\f872"; } +.bi-building-fill-slash::before { content: "\f873"; } +.bi-building-fill-up::before { content: "\f874"; } +.bi-building-fill-x::before { content: "\f875"; } +.bi-building-fill::before { content: "\f876"; } +.bi-building-gear::before { content: "\f877"; } +.bi-building-lock::before { content: "\f878"; } +.bi-building-slash::before { content: "\f879"; } +.bi-building-up::before { content: "\f87a"; } +.bi-building-x::before { content: "\f87b"; } +.bi-buildings-fill::before { content: "\f87c"; } +.bi-buildings::before { content: "\f87d"; } +.bi-bus-front-fill::before { content: "\f87e"; } +.bi-bus-front::before { content: "\f87f"; } +.bi-ev-front-fill::before { content: "\f880"; } +.bi-ev-front::before { content: "\f881"; } +.bi-globe-americas::before { content: "\f882"; } +.bi-globe-asia-australia::before { content: "\f883"; } +.bi-globe-central-south-asia::before { content: "\f884"; } +.bi-globe-europe-africa::before { content: "\f885"; } +.bi-house-add-fill::before { content: "\f886"; } +.bi-house-add::before { content: "\f887"; } +.bi-house-check-fill::before { content: "\f888"; } +.bi-house-check::before { content: "\f889"; } +.bi-house-dash-fill::before { content: "\f88a"; } +.bi-house-dash::before { content: "\f88b"; } +.bi-house-down-fill::before { content: "\f88c"; } +.bi-house-down::before { content: "\f88d"; } +.bi-house-exclamation-fill::before { content: "\f88e"; } +.bi-house-exclamation::before { content: "\f88f"; } +.bi-house-gear-fill::before { content: "\f890"; } +.bi-house-gear::before { content: "\f891"; } +.bi-house-lock-fill::before { content: "\f892"; } +.bi-house-lock::before { content: "\f893"; } +.bi-house-slash-fill::before { content: "\f894"; } +.bi-house-slash::before { content: "\f895"; } +.bi-house-up-fill::before { content: "\f896"; } +.bi-house-up::before { content: "\f897"; } +.bi-house-x-fill::before { content: "\f898"; } +.bi-house-x::before { content: "\f899"; } +.bi-person-add::before { content: "\f89a"; } +.bi-person-down::before { content: "\f89b"; } +.bi-person-exclamation::before { content: "\f89c"; } +.bi-person-fill-add::before { content: "\f89d"; } +.bi-person-fill-check::before { content: "\f89e"; } +.bi-person-fill-dash::before { content: "\f89f"; } +.bi-person-fill-down::before { content: "\f8a0"; } +.bi-person-fill-exclamation::before { content: "\f8a1"; } +.bi-person-fill-gear::before { content: "\f8a2"; } +.bi-person-fill-lock::before { content: "\f8a3"; } +.bi-person-fill-slash::before { content: "\f8a4"; } +.bi-person-fill-up::before { content: "\f8a5"; } +.bi-person-fill-x::before { content: "\f8a6"; } +.bi-person-gear::before { content: "\f8a7"; } +.bi-person-lock::before { content: "\f8a8"; } +.bi-person-slash::before { content: "\f8a9"; } +.bi-person-up::before { content: "\f8aa"; } +.bi-scooter::before { content: "\f8ab"; } +.bi-taxi-front-fill::before { content: "\f8ac"; } +.bi-taxi-front::before { content: "\f8ad"; } +.bi-amd::before { content: "\f8ae"; } +.bi-database-add::before { content: "\f8af"; } +.bi-database-check::before { content: "\f8b0"; } +.bi-database-dash::before { content: "\f8b1"; } +.bi-database-down::before { content: "\f8b2"; } +.bi-database-exclamation::before { content: "\f8b3"; } +.bi-database-fill-add::before { content: "\f8b4"; } +.bi-database-fill-check::before { content: "\f8b5"; } +.bi-database-fill-dash::before { content: "\f8b6"; } +.bi-database-fill-down::before { content: "\f8b7"; } +.bi-database-fill-exclamation::before { content: "\f8b8"; } +.bi-database-fill-gear::before { content: "\f8b9"; } +.bi-database-fill-lock::before { content: "\f8ba"; } +.bi-database-fill-slash::before { content: "\f8bb"; } +.bi-database-fill-up::before { content: "\f8bc"; } +.bi-database-fill-x::before { content: "\f8bd"; } +.bi-database-fill::before { content: "\f8be"; } +.bi-database-gear::before { content: "\f8bf"; } +.bi-database-lock::before { content: "\f8c0"; } +.bi-database-slash::before { content: "\f8c1"; } +.bi-database-up::before { content: "\f8c2"; } +.bi-database-x::before { content: "\f8c3"; } +.bi-database::before { content: "\f8c4"; } +.bi-houses-fill::before { content: "\f8c5"; } +.bi-houses::before { content: "\f8c6"; } +.bi-nvidia::before { content: "\f8c7"; } +.bi-person-vcard-fill::before { content: "\f8c8"; } +.bi-person-vcard::before { content: "\f8c9"; } +.bi-sina-weibo::before { content: "\f8ca"; } +.bi-tencent-qq::before { content: "\f8cb"; } +.bi-wikipedia::before { content: "\f8cc"; } +.bi-alphabet-uppercase::before { content: "\f2a5"; } +.bi-alphabet::before { content: "\f68a"; } +.bi-amazon::before { content: "\f68d"; } +.bi-arrows-collapse-vertical::before { content: "\f690"; } +.bi-arrows-expand-vertical::before { content: "\f695"; } +.bi-arrows-vertical::before { content: "\f698"; } +.bi-arrows::before { content: "\f6a2"; } +.bi-ban-fill::before { content: "\f6a3"; } +.bi-ban::before { content: "\f6b6"; } +.bi-bing::before { content: "\f6c2"; } +.bi-cake::before { content: "\f6e0"; } +.bi-cake2::before { content: "\f6ed"; } +.bi-cookie::before { content: "\f6ee"; } +.bi-copy::before { content: "\f759"; } +.bi-crosshair::before { content: "\f769"; } +.bi-crosshair2::before { content: "\f794"; } +.bi-emoji-astonished-fill::before { content: "\f795"; } +.bi-emoji-astonished::before { content: "\f79a"; } +.bi-emoji-grimace-fill::before { content: "\f79b"; } +.bi-emoji-grimace::before { content: "\f7a0"; } +.bi-emoji-grin-fill::before { content: "\f7a1"; } +.bi-emoji-grin::before { content: "\f7a6"; } +.bi-emoji-surprise-fill::before { content: "\f7a7"; } +.bi-emoji-surprise::before { content: "\f7ac"; } +.bi-emoji-tear-fill::before { content: "\f7ad"; } +.bi-emoji-tear::before { content: "\f7b2"; } +.bi-envelope-arrow-down-fill::before { content: "\f7b3"; } +.bi-envelope-arrow-down::before { content: "\f7b8"; } +.bi-envelope-arrow-up-fill::before { content: "\f7b9"; } +.bi-envelope-arrow-up::before { content: "\f7be"; } +.bi-feather::before { content: "\f7bf"; } +.bi-feather2::before { content: "\f7c4"; } +.bi-floppy-fill::before { content: "\f7c5"; } +.bi-floppy::before { content: "\f7d8"; } +.bi-floppy2-fill::before { content: "\f7d9"; } +.bi-floppy2::before { content: "\f7e4"; } +.bi-gitlab::before { content: "\f7e5"; } +.bi-highlighter::before { content: "\f7f8"; } +.bi-marker-tip::before { content: "\f802"; } +.bi-nvme-fill::before { content: "\f803"; } +.bi-nvme::before { content: "\f80c"; } +.bi-opencollective::before { content: "\f80d"; } +.bi-pci-card-network::before { content: "\f8cd"; } +.bi-pci-card-sound::before { content: "\f8ce"; } +.bi-radar::before { content: "\f8cf"; } +.bi-send-arrow-down-fill::before { content: "\f8d0"; } +.bi-send-arrow-down::before { content: "\f8d1"; } +.bi-send-arrow-up-fill::before { content: "\f8d2"; } +.bi-send-arrow-up::before { content: "\f8d3"; } +.bi-sim-slash-fill::before { content: "\f8d4"; } +.bi-sim-slash::before { content: "\f8d5"; } +.bi-sourceforge::before { content: "\f8d6"; } +.bi-substack::before { content: "\f8d7"; } +.bi-threads-fill::before { content: "\f8d8"; } +.bi-threads::before { content: "\f8d9"; } +.bi-transparency::before { content: "\f8da"; } +.bi-twitter-x::before { content: "\f8db"; } +.bi-type-h4::before { content: "\f8dc"; } +.bi-type-h5::before { content: "\f8dd"; } +.bi-type-h6::before { content: "\f8de"; } +.bi-backpack-fill::before { content: "\f8df"; } +.bi-backpack::before { content: "\f8e0"; } +.bi-backpack2-fill::before { content: "\f8e1"; } +.bi-backpack2::before { content: "\f8e2"; } +.bi-backpack3-fill::before { content: "\f8e3"; } +.bi-backpack3::before { content: "\f8e4"; } +.bi-backpack4-fill::before { content: "\f8e5"; } +.bi-backpack4::before { content: "\f8e6"; } +.bi-brilliance::before { content: "\f8e7"; } +.bi-cake-fill::before { content: "\f8e8"; } +.bi-cake2-fill::before { content: "\f8e9"; } +.bi-duffle-fill::before { content: "\f8ea"; } +.bi-duffle::before { content: "\f8eb"; } +.bi-exposure::before { content: "\f8ec"; } +.bi-gender-neuter::before { content: "\f8ed"; } +.bi-highlights::before { content: "\f8ee"; } +.bi-luggage-fill::before { content: "\f8ef"; } +.bi-luggage::before { content: "\f8f0"; } +.bi-mailbox-flag::before { content: "\f8f1"; } +.bi-mailbox2-flag::before { content: "\f8f2"; } +.bi-noise-reduction::before { content: "\f8f3"; } +.bi-passport-fill::before { content: "\f8f4"; } +.bi-passport::before { content: "\f8f5"; } +.bi-person-arms-up::before { content: "\f8f6"; } +.bi-person-raised-hand::before { content: "\f8f7"; } +.bi-person-standing-dress::before { content: "\f8f8"; } +.bi-person-standing::before { content: "\f8f9"; } +.bi-person-walking::before { content: "\f8fa"; } +.bi-person-wheelchair::before { content: "\f8fb"; } +.bi-shadows::before { content: "\f8fc"; } +.bi-suitcase-fill::before { content: "\f8fd"; } +.bi-suitcase-lg-fill::before { content: "\f8fe"; } +.bi-suitcase-lg::before { content: "\f8ff"; } +.bi-suitcase::before { content: "\f900"; } +.bi-suitcase2-fill::before { content: "\f901"; } +.bi-suitcase2::before { content: "\f902"; } +.bi-vignette::before { content: "\f903"; } diff --git a/docs/site_libs/bootstrap/bootstrap-icons.woff b/docs/site_libs/bootstrap/bootstrap-icons.woff new file mode 100644 index 0000000..dbeeb05 Binary files /dev/null and b/docs/site_libs/bootstrap/bootstrap-icons.woff differ diff --git a/docs/site_libs/bootstrap/bootstrap.min.css b/docs/site_libs/bootstrap/bootstrap.min.css new file mode 100644 index 0000000..e4511a5 --- /dev/null +++ b/docs/site_libs/bootstrap/bootstrap.min.css @@ -0,0 +1,12 @@ +/*! + * Bootstrap v5.3.1 (https://getbootstrap.com/) + * Copyright 2011-2023 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root,[data-bs-theme=light]{--bs-blue: #0d6efd;--bs-indigo: #6610f2;--bs-purple: #6f42c1;--bs-pink: #d63384;--bs-red: #dc3545;--bs-orange: #fd7e14;--bs-yellow: #ffc107;--bs-green: #198754;--bs-teal: #20c997;--bs-cyan: #0dcaf0;--bs-black: #000;--bs-white: #ffffff;--bs-gray: #6c757d;--bs-gray-dark: #343a40;--bs-gray-100: #f8f9fa;--bs-gray-200: #e9ecef;--bs-gray-300: #dee2e6;--bs-gray-400: #ced4da;--bs-gray-500: #adb5bd;--bs-gray-600: #6c757d;--bs-gray-700: #495057;--bs-gray-800: #343a40;--bs-gray-900: #212529;--bs-default: #dee2e6;--bs-primary: #0d6efd;--bs-secondary: #6c757d;--bs-success: #198754;--bs-info: #0dcaf0;--bs-warning: #ffc107;--bs-danger: #dc3545;--bs-light: #f8f9fa;--bs-dark: #212529;--bs-default-rgb: 222, 226, 230;--bs-primary-rgb: 13, 110, 253;--bs-secondary-rgb: 108, 117, 125;--bs-success-rgb: 25, 135, 84;--bs-info-rgb: 13, 202, 240;--bs-warning-rgb: 255, 193, 7;--bs-danger-rgb: 220, 53, 69;--bs-light-rgb: 248, 249, 250;--bs-dark-rgb: 33, 37, 41;--bs-primary-text-emphasis: #052c65;--bs-secondary-text-emphasis: #2b2f32;--bs-success-text-emphasis: #0a3622;--bs-info-text-emphasis: #055160;--bs-warning-text-emphasis: #664d03;--bs-danger-text-emphasis: #58151c;--bs-light-text-emphasis: #495057;--bs-dark-text-emphasis: #495057;--bs-primary-bg-subtle: #cfe2ff;--bs-secondary-bg-subtle: #e2e3e5;--bs-success-bg-subtle: #d1e7dd;--bs-info-bg-subtle: #cff4fc;--bs-warning-bg-subtle: #fff3cd;--bs-danger-bg-subtle: #f8d7da;--bs-light-bg-subtle: #fcfcfd;--bs-dark-bg-subtle: #ced4da;--bs-primary-border-subtle: #9ec5fe;--bs-secondary-border-subtle: #c4c8cb;--bs-success-border-subtle: #a3cfbb;--bs-info-border-subtle: #9eeaf9;--bs-warning-border-subtle: #ffe69c;--bs-danger-border-subtle: #f1aeb5;--bs-light-border-subtle: #e9ecef;--bs-dark-border-subtle: #adb5bd;--bs-white-rgb: 255, 255, 255;--bs-black-rgb: 0, 0, 0;--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-root-font-size: 17px;--bs-body-font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--bs-body-font-size:1rem;--bs-body-font-weight: 400;--bs-body-line-height: 1.5;--bs-body-color: #212529;--bs-body-color-rgb: 33, 37, 41;--bs-body-bg: #ffffff;--bs-body-bg-rgb: 255, 255, 255;--bs-emphasis-color: #000;--bs-emphasis-color-rgb: 0, 0, 0;--bs-secondary-color: rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb: 33, 37, 41;--bs-secondary-bg: #e9ecef;--bs-secondary-bg-rgb: 233, 236, 239;--bs-tertiary-color: rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb: 33, 37, 41;--bs-tertiary-bg: #f8f9fa;--bs-tertiary-bg-rgb: 248, 249, 250;--bs-heading-color: inherit;--bs-link-color: #0d6efd;--bs-link-color-rgb: 13, 110, 253;--bs-link-decoration: underline;--bs-link-hover-color: #0a58ca;--bs-link-hover-color-rgb: 10, 88, 202;--bs-code-color: #7d12ba;--bs-highlight-bg: #fff3cd;--bs-border-width: 1px;--bs-border-style: solid;--bs-border-color: #dee2e6;--bs-border-color-translucent: rgba(0, 0, 0, 0.175);--bs-border-radius: 0.25rem;--bs-border-radius-sm: 0.2em;--bs-border-radius-lg: 0.5rem;--bs-border-radius-xl: 1rem;--bs-border-radius-xxl: 2rem;--bs-border-radius-2xl: var(--bs-border-radius-xxl);--bs-border-radius-pill: 50rem;--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width: 0.25rem;--bs-focus-ring-opacity: 0.25;--bs-focus-ring-color: rgba(13, 110, 253, 0.25);--bs-form-valid-color: #198754;--bs-form-valid-border-color: #198754;--bs-form-invalid-color: #dc3545;--bs-form-invalid-border-color: #dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color: #dee2e6;--bs-body-color-rgb: 222, 226, 230;--bs-body-bg: #212529;--bs-body-bg-rgb: 33, 37, 41;--bs-emphasis-color: #ffffff;--bs-emphasis-color-rgb: 255, 255, 255;--bs-secondary-color: rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb: 222, 226, 230;--bs-secondary-bg: #343a40;--bs-secondary-bg-rgb: 52, 58, 64;--bs-tertiary-color: rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb: 222, 226, 230;--bs-tertiary-bg: #2b3035;--bs-tertiary-bg-rgb: 43, 48, 53;--bs-primary-text-emphasis: #6ea8fe;--bs-secondary-text-emphasis: #a7acb1;--bs-success-text-emphasis: #75b798;--bs-info-text-emphasis: #6edff6;--bs-warning-text-emphasis: #ffda6a;--bs-danger-text-emphasis: #ea868f;--bs-light-text-emphasis: #f8f9fa;--bs-dark-text-emphasis: #dee2e6;--bs-primary-bg-subtle: #031633;--bs-secondary-bg-subtle: #161719;--bs-success-bg-subtle: #051b11;--bs-info-bg-subtle: #032830;--bs-warning-bg-subtle: #332701;--bs-danger-bg-subtle: #2c0b0e;--bs-light-bg-subtle: #343a40;--bs-dark-bg-subtle: #1a1d20;--bs-primary-border-subtle: #084298;--bs-secondary-border-subtle: #41464b;--bs-success-border-subtle: #0f5132;--bs-info-border-subtle: #087990;--bs-warning-border-subtle: #997404;--bs-danger-border-subtle: #842029;--bs-light-border-subtle: #495057;--bs-dark-border-subtle: #343a40;--bs-heading-color: inherit;--bs-link-color: #6ea8fe;--bs-link-hover-color: #8bb9fe;--bs-link-color-rgb: 110, 168, 254;--bs-link-hover-color-rgb: 139, 185, 254;--bs-code-color: white;--bs-border-color: #495057;--bs-border-color-translucent: rgba(255, 255, 255, 0.15);--bs-form-valid-color: #75b798;--bs-form-valid-border-color: #75b798;--bs-form-invalid-color: #ea868f;--bs-form-invalid-border-color: #ea868f}*,*::before,*::after{box-sizing:border-box}:root{font-size:var(--bs-root-font-size)}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}hr{margin:1rem 0;color:inherit;border:0;border-top:1px solid;opacity:.25}h6,.h6,h5,.h5,h4,.h4,h3,.h3,h2,.h2,h1,.h1{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2;color:var(--bs-heading-color)}h1,.h1{font-size:calc(1.325rem + 0.9vw)}@media(min-width: 1200px){h1,.h1{font-size:2rem}}h2,.h2{font-size:calc(1.29rem + 0.48vw)}@media(min-width: 1200px){h2,.h2{font-size:1.65rem}}h3,.h3{font-size:calc(1.27rem + 0.24vw)}@media(min-width: 1200px){h3,.h3{font-size:1.45rem}}h4,.h4{font-size:1.25rem}h5,.h5{font-size:1.1rem}h6,.h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{text-decoration:underline dotted;-webkit-text-decoration:underline dotted;-moz-text-decoration:underline dotted;-ms-text-decoration:underline dotted;-o-text-decoration:underline dotted;cursor:help;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem;padding:.625rem 1.25rem;border-left:.25rem solid #e9ecef}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}b,strong{font-weight:bolder}small,.small{font-size:0.875em}mark,.mark{padding:.1875em;background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:0.75em;line-height:0;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}a{color:rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));text-decoration:underline;-webkit-text-decoration:underline;-moz-text-decoration:underline;-ms-text-decoration:underline;-o-text-decoration:underline}a:hover{--bs-link-color-rgb: var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}pre,code,kbd,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:0.875em;color:#000;background-color:#f8f9fa;padding:.5rem;border:1px solid var(--bs-border-color, #dee2e6);border-radius:.25rem}pre code{background-color:rgba(0,0,0,0);font-size:inherit;color:inherit;word-break:normal}code{font-size:0.875em;color:var(--bs-code-color);background-color:#f8f9fa;border-radius:.25rem;padding:.125rem .25rem;word-wrap:break-word}a>code{color:inherit}kbd{padding:.4rem .4rem;font-size:0.875em;color:#fff;background-color:#212529;border-radius:.2em}kbd kbd{padding:0;font-size:1em}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:rgba(33,37,41,.75);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]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none !important}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]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-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:rgba(33,37,41,.75)}.container,.container-fluid,.container-xxl,.container-xl,.container-lg,.container-md,.container-sm{--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;width:100%;padding-right:calc(var(--bs-gutter-x)*.5);padding-left:calc(var(--bs-gutter-x)*.5);margin-right:auto;margin-left:auto}@media(min-width: 576px){.container-sm,.container{max-width:540px}}@media(min-width: 768px){.container-md,.container-sm,.container{max-width:720px}}@media(min-width: 992px){.container-lg,.container-md,.container-sm,.container{max-width:960px}}@media(min-width: 1200px){.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1140px}}@media(min-width: 1400px){.container-xxl,.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1320px}}:root{--bs-breakpoint-xs: 0;--bs-breakpoint-sm: 576px;--bs-breakpoint-md: 768px;--bs-breakpoint-lg: 992px;--bs-breakpoint-xl: 1200px;--bs-breakpoint-xxl: 1400px}.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-color-type: initial;--bs-table-bg-type: initial;--bs-table-color-state: initial;--bs-table-bg-state: initial;--bs-table-color: #212529;--bs-table-bg: #ffffff;--bs-table-border-color: #dee2e6;--bs-table-accent-bg: transparent;--bs-table-striped-color: #212529;--bs-table-striped-bg: rgba(0, 0, 0, 0.05);--bs-table-active-color: #212529;--bs-table-active-bg: rgba(0, 0, 0, 0.1);--bs-table-hover-color: #212529;--bs-table-hover-bg: rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem .5rem;color:var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg)))}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(1px*2) solid #9ba5ae}.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-color-type: var(--bs-table-striped-color);--bs-table-bg-type: var(--bs-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(even){--bs-table-color-type: var(--bs-table-striped-color);--bs-table-bg-type: var(--bs-table-striped-bg)}.table-active{--bs-table-color-state: var(--bs-table-active-color);--bs-table-bg-state: var(--bs-table-active-bg)}.table-hover>tbody>tr:hover>*{--bs-table-color-state: var(--bs-table-hover-color);--bs-table-bg-state: var(--bs-table-hover-bg)}.table-primary{--bs-table-color: #000;--bs-table-bg: #cfe2ff;--bs-table-border-color: #bacbe6;--bs-table-striped-bg: #c5d7f2;--bs-table-striped-color: #000;--bs-table-active-bg: #bacbe6;--bs-table-active-color: #000;--bs-table-hover-bg: #bfd1ec;--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color: #000;--bs-table-bg: #e2e3e5;--bs-table-border-color: #cbccce;--bs-table-striped-bg: #d7d8da;--bs-table-striped-color: #000;--bs-table-active-bg: #cbccce;--bs-table-active-color: #000;--bs-table-hover-bg: #d1d2d4;--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color: #000;--bs-table-bg: #d1e7dd;--bs-table-border-color: #bcd0c7;--bs-table-striped-bg: #c7dbd2;--bs-table-striped-color: #000;--bs-table-active-bg: #bcd0c7;--bs-table-active-color: #000;--bs-table-hover-bg: #c1d6cc;--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color: #000;--bs-table-bg: #cff4fc;--bs-table-border-color: #badce3;--bs-table-striped-bg: #c5e8ef;--bs-table-striped-color: #000;--bs-table-active-bg: #badce3;--bs-table-active-color: #000;--bs-table-hover-bg: #bfe2e9;--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color: #000;--bs-table-bg: #fff3cd;--bs-table-border-color: #e6dbb9;--bs-table-striped-bg: #f2e7c3;--bs-table-striped-color: #000;--bs-table-active-bg: #e6dbb9;--bs-table-active-color: #000;--bs-table-hover-bg: #ece1be;--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color: #000;--bs-table-bg: #f8d7da;--bs-table-border-color: #dfc2c4;--bs-table-striped-bg: #eccccf;--bs-table-striped-color: #000;--bs-table-active-bg: #dfc2c4;--bs-table-active-color: #000;--bs-table-hover-bg: #e5c7ca;--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color: #000;--bs-table-bg: #f8f9fa;--bs-table-border-color: #dfe0e1;--bs-table-striped-bg: #ecedee;--bs-table-striped-color: #000;--bs-table-active-bg: #dfe0e1;--bs-table-active-color: #000;--bs-table-hover-bg: #e5e6e7;--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color: #ffffff;--bs-table-bg: #212529;--bs-table-border-color: #373b3e;--bs-table-striped-bg: #2c3034;--bs-table-striped-color: #ffffff;--bs-table-active-bg: #373b3e;--bs-table-active-color: #ffffff;--bs-table-hover-bg: #323539;--bs-table-hover-color: #ffffff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.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:rgba(33,37,41,.75)}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;background-color:#fff;background-clip:padding-box;border:1px solid #dee2e6;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:#212529;background-color:#fff;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::placeholder{color:rgba(33,37,41,.75);opacity:1}.form-control:disabled{background-color:#e9ecef;opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-0.375rem -0.75rem;margin-inline-end:.75rem;color:#212529;background-color:#f8f9fa;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:#e9ecef}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#212529;background-color:rgba(0,0,0,0);border:solid rgba(0,0,0,0);border-width:1px 0}.form-control-plaintext:focus{outline: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 + calc(1px * 2));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-lg{min-height:calc(1.5em + 1rem + calc(1px * 2));padding:.5rem 1rem;font-size:1.25rem;border-radius:.5rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-0.5rem -1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + 0.75rem + calc(1px * 2))}textarea.form-control-sm{min-height:calc(1.5em + 0.5rem + calc(1px * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(1px * 2))}.form-control-color{width:3rem;height:calc(1.5em + 0.75rem + calc(1px * 2));padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0 !important;border-radius:.25rem}.form-control-color::-webkit-color-swatch{border:0 !important;border-radius:.25rem}.form-control-color.form-control-sm{height:calc(1.5em + 0.5rem + calc(1px * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(1px * 2))}.form-select{--bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;background-color:#fff;background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon, none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #dee2e6;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:rgba(0,0,0,0);text-shadow:0 0 0 #212529}.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:.5rem}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.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-reverse{padding-right:0;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:0;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{--bs-form-check-bg: #ffffff;width:1em;height:1em;margin-top:.25em;vertical-align:top;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid #dee2e6;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:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.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:#0d6efd;border-color:#0d6efd}.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]{--bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-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]{--bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23ffffff'/%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:#0d6efd;border-color:#0d6efd;--bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23ffffff' 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{cursor:default;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{--bs-form-switch-bg: 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");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);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{--bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;--bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23ffffff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-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}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;background-color:rgba(0,0,0,0)}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-0.25rem;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-range::-webkit-slider-thumb{transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:rgba(0,0,0,0);cursor:pointer;background-color:#f8f9fa;border-color:rgba(0,0,0,0);border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-range::-moz-range-thumb{transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:rgba(0,0,0,0);cursor:pointer;background-color:#f8f9fa;border-color:rgba(0,0,0,0);border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:rgba(33,37,41,.75)}.form-range:disabled::-moz-range-thumb{background-color:rgba(33,37,41,.75)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(1px * 2));min-height:calc(3.5rem + calc(1px * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;height:100%;padding:1rem .75rem;overflow:hidden;text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:1px solid rgba(0,0,0,0);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,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control::placeholder,.form-floating>.form-control-plaintext::placeholder{color:rgba(0,0,0,0)}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown),.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill,.form-floating>.form-control-plaintext:-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-control-plaintext~label,.form-floating>.form-select~label{color:rgba(var(--bs-body-color-rgb), 0.65);transform:scale(0.85) translateY(-0.5rem) translateX(0.15rem)}.form-floating>.form-control:focus~label::after,.form-floating>.form-control:not(:placeholder-shown)~label::after,.form-floating>.form-control-plaintext~label::after,.form-floating>.form-select~label::after{position:absolute;inset:1rem .375rem;z-index:-1;height:1.5em;content:"";background-color:#fff;border-radius:.25rem}.form-floating>.form-control:-webkit-autofill~label{color:rgba(var(--bs-body-color-rgb), 0.65);transform:scale(0.85) translateY(-0.5rem) translateX(0.15rem)}.form-floating>.form-control-plaintext~label{border-width:1px 0}.form-floating>:disabled~label,.form-floating>.form-control:disabled~label{color:#6c757d}.form-floating>:disabled~label::after,.form-floating>.form-control:disabled~label::after{background-color:#e9ecef}.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,.input-group>.form-floating{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,.input-group>.form-floating:focus-within{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.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:#212529;text-align:center;white-space:nowrap;background-color:#f8f9fa;border:1px solid #dee2e6;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:.5rem}.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):not(.form-floating),.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select{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):not(.form-floating),.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select{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:calc(1px*-1);border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:0.875em;color:#198754}.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:#198754;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:#198754;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='%23198754' d='M2.3 6.73.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:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.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:#198754}.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"]{--bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.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");padding-right:4.125rem;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:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated .form-control-color:valid,.form-control-color.is-valid{width:calc(3rem + calc(1.5em + 0.75rem))}.was-validated .form-check-input:valid,.form-check-input.is-valid{border-color:#198754}.was-validated .form-check-input:valid:checked,.form-check-input.is-valid:checked{background-color:#198754}.was-validated .form-check-input:valid:focus,.form-check-input.is-valid:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated .form-check-input:valid~.form-check-label,.form-check-input.is-valid~.form-check-label{color:#198754}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):valid,.input-group>.form-control:not(:focus).is-valid,.was-validated .input-group>.form-select:not(:focus):valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.input-group>.form-floating:not(:focus-within).is-valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:0.875em;color:#dc3545}.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:#dc3545;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:#dc3545;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='%23dc3545'%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='%23dc3545' 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:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.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:#dc3545}.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"]{--bs-form-select-bg-icon: 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='%23dc3545'%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='%23dc3545' stroke='none'/%3e%3c/svg%3e");padding-right:4.125rem;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:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated .form-control-color:invalid,.form-control-color.is-invalid{width:calc(3rem + calc(1.5em + 0.75rem))}.was-validated .form-check-input:invalid,.form-check-input.is-invalid{border-color:#dc3545}.was-validated .form-check-input:invalid:checked,.form-check-input.is-invalid:checked{background-color:#dc3545}.was-validated .form-check-input:invalid:focus,.form-check-input.is-invalid:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated .form-check-input:invalid~.form-check-label,.form-check-input.is-invalid~.form-check-label{color:#dc3545}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):invalid,.input-group>.form-control:not(:focus).is-invalid,.was-validated .input-group>.form-select:not(:focus):invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.input-group>.form-floating:not(:focus-within).is-invalid{z-index:4}.btn{--bs-btn-padding-x: 0.75rem;--bs-btn-padding-y: 0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight: 400;--bs-btn-line-height: 1.5;--bs-btn-color: #212529;--bs-btn-bg: transparent;--bs-btn-border-width: 1px;--bs-btn-border-color: transparent;--bs-btn-border-radius: 0.25rem;--bs-btn-hover-border-color: transparent;--bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity: 0.65;--bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);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;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);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:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,:not(.btn-check)+.btn:active,.btn:first-child:active,.btn.active,.btn.show{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,:not(.btn-check)+.btn:active:focus-visible,.btn:first-child:active:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn:disabled,.btn.disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-default{--bs-btn-color: #000;--bs-btn-bg: #dee2e6;--bs-btn-border-color: #dee2e6;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #e3e6ea;--bs-btn-hover-border-color: #e1e5e9;--bs-btn-focus-shadow-rgb: 189, 192, 196;--bs-btn-active-color: #000;--bs-btn-active-bg: #e5e8eb;--bs-btn-active-border-color: #e1e5e9;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #000;--bs-btn-disabled-bg: #dee2e6;--bs-btn-disabled-border-color: #dee2e6}.btn-primary{--bs-btn-color: #ffffff;--bs-btn-bg: #0d6efd;--bs-btn-border-color: #0d6efd;--bs-btn-hover-color: #ffffff;--bs-btn-hover-bg: #0b5ed7;--bs-btn-hover-border-color: #0a58ca;--bs-btn-focus-shadow-rgb: 49, 132, 253;--bs-btn-active-color: #ffffff;--bs-btn-active-bg: #0a58ca;--bs-btn-active-border-color: #0a53be;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #ffffff;--bs-btn-disabled-bg: #0d6efd;--bs-btn-disabled-border-color: #0d6efd}.btn-secondary{--bs-btn-color: #ffffff;--bs-btn-bg: #6c757d;--bs-btn-border-color: #6c757d;--bs-btn-hover-color: #ffffff;--bs-btn-hover-bg: #5c636a;--bs-btn-hover-border-color: #565e64;--bs-btn-focus-shadow-rgb: 130, 138, 145;--bs-btn-active-color: #ffffff;--bs-btn-active-bg: #565e64;--bs-btn-active-border-color: #51585e;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #ffffff;--bs-btn-disabled-bg: #6c757d;--bs-btn-disabled-border-color: #6c757d}.btn-success{--bs-btn-color: #ffffff;--bs-btn-bg: #198754;--bs-btn-border-color: #198754;--bs-btn-hover-color: #ffffff;--bs-btn-hover-bg: #157347;--bs-btn-hover-border-color: #146c43;--bs-btn-focus-shadow-rgb: 60, 153, 110;--bs-btn-active-color: #ffffff;--bs-btn-active-bg: #146c43;--bs-btn-active-border-color: #13653f;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #ffffff;--bs-btn-disabled-bg: #198754;--bs-btn-disabled-border-color: #198754}.btn-info{--bs-btn-color: #000;--bs-btn-bg: #0dcaf0;--bs-btn-border-color: #0dcaf0;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #31d2f2;--bs-btn-hover-border-color: #25cff2;--bs-btn-focus-shadow-rgb: 11, 172, 204;--bs-btn-active-color: #000;--bs-btn-active-bg: #3dd5f3;--bs-btn-active-border-color: #25cff2;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #000;--bs-btn-disabled-bg: #0dcaf0;--bs-btn-disabled-border-color: #0dcaf0}.btn-warning{--bs-btn-color: #000;--bs-btn-bg: #ffc107;--bs-btn-border-color: #ffc107;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #ffca2c;--bs-btn-hover-border-color: #ffc720;--bs-btn-focus-shadow-rgb: 217, 164, 6;--bs-btn-active-color: #000;--bs-btn-active-bg: #ffcd39;--bs-btn-active-border-color: #ffc720;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #000;--bs-btn-disabled-bg: #ffc107;--bs-btn-disabled-border-color: #ffc107}.btn-danger{--bs-btn-color: #ffffff;--bs-btn-bg: #dc3545;--bs-btn-border-color: #dc3545;--bs-btn-hover-color: #ffffff;--bs-btn-hover-bg: #bb2d3b;--bs-btn-hover-border-color: #b02a37;--bs-btn-focus-shadow-rgb: 225, 83, 97;--bs-btn-active-color: #ffffff;--bs-btn-active-bg: #b02a37;--bs-btn-active-border-color: #a52834;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #ffffff;--bs-btn-disabled-bg: #dc3545;--bs-btn-disabled-border-color: #dc3545}.btn-light{--bs-btn-color: #000;--bs-btn-bg: #f8f9fa;--bs-btn-border-color: #f8f9fa;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #d3d4d5;--bs-btn-hover-border-color: #c6c7c8;--bs-btn-focus-shadow-rgb: 211, 212, 213;--bs-btn-active-color: #000;--bs-btn-active-bg: #c6c7c8;--bs-btn-active-border-color: #babbbc;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #000;--bs-btn-disabled-bg: #f8f9fa;--bs-btn-disabled-border-color: #f8f9fa}.btn-dark{--bs-btn-color: #ffffff;--bs-btn-bg: #212529;--bs-btn-border-color: #212529;--bs-btn-hover-color: #ffffff;--bs-btn-hover-bg: #424649;--bs-btn-hover-border-color: #373b3e;--bs-btn-focus-shadow-rgb: 66, 70, 73;--bs-btn-active-color: #ffffff;--bs-btn-active-bg: #4d5154;--bs-btn-active-border-color: #373b3e;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #ffffff;--bs-btn-disabled-bg: #212529;--bs-btn-disabled-border-color: #212529}.btn-outline-default{--bs-btn-color: #dee2e6;--bs-btn-border-color: #dee2e6;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #dee2e6;--bs-btn-hover-border-color: #dee2e6;--bs-btn-focus-shadow-rgb: 222, 226, 230;--bs-btn-active-color: #000;--bs-btn-active-bg: #dee2e6;--bs-btn-active-border-color: #dee2e6;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #dee2e6;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #dee2e6;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-primary{--bs-btn-color: #0d6efd;--bs-btn-border-color: #0d6efd;--bs-btn-hover-color: #ffffff;--bs-btn-hover-bg: #0d6efd;--bs-btn-hover-border-color: #0d6efd;--bs-btn-focus-shadow-rgb: 13, 110, 253;--bs-btn-active-color: #ffffff;--bs-btn-active-bg: #0d6efd;--bs-btn-active-border-color: #0d6efd;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #0d6efd;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #0d6efd;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-secondary{--bs-btn-color: #6c757d;--bs-btn-border-color: #6c757d;--bs-btn-hover-color: #ffffff;--bs-btn-hover-bg: #6c757d;--bs-btn-hover-border-color: #6c757d;--bs-btn-focus-shadow-rgb: 108, 117, 125;--bs-btn-active-color: #ffffff;--bs-btn-active-bg: #6c757d;--bs-btn-active-border-color: #6c757d;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #6c757d;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #6c757d;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-success{--bs-btn-color: #198754;--bs-btn-border-color: #198754;--bs-btn-hover-color: #ffffff;--bs-btn-hover-bg: #198754;--bs-btn-hover-border-color: #198754;--bs-btn-focus-shadow-rgb: 25, 135, 84;--bs-btn-active-color: #ffffff;--bs-btn-active-bg: #198754;--bs-btn-active-border-color: #198754;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #198754;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #198754;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-info{--bs-btn-color: #0dcaf0;--bs-btn-border-color: #0dcaf0;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #0dcaf0;--bs-btn-hover-border-color: #0dcaf0;--bs-btn-focus-shadow-rgb: 13, 202, 240;--bs-btn-active-color: #000;--bs-btn-active-bg: #0dcaf0;--bs-btn-active-border-color: #0dcaf0;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #0dcaf0;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #0dcaf0;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-warning{--bs-btn-color: #ffc107;--bs-btn-border-color: #ffc107;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #ffc107;--bs-btn-hover-border-color: #ffc107;--bs-btn-focus-shadow-rgb: 255, 193, 7;--bs-btn-active-color: #000;--bs-btn-active-bg: #ffc107;--bs-btn-active-border-color: #ffc107;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #ffc107;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #ffc107;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-danger{--bs-btn-color: #dc3545;--bs-btn-border-color: #dc3545;--bs-btn-hover-color: #ffffff;--bs-btn-hover-bg: #dc3545;--bs-btn-hover-border-color: #dc3545;--bs-btn-focus-shadow-rgb: 220, 53, 69;--bs-btn-active-color: #ffffff;--bs-btn-active-bg: #dc3545;--bs-btn-active-border-color: #dc3545;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #dc3545;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #dc3545;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-light{--bs-btn-color: #f8f9fa;--bs-btn-border-color: #f8f9fa;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #f8f9fa;--bs-btn-hover-border-color: #f8f9fa;--bs-btn-focus-shadow-rgb: 248, 249, 250;--bs-btn-active-color: #000;--bs-btn-active-bg: #f8f9fa;--bs-btn-active-border-color: #f8f9fa;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #f8f9fa;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #f8f9fa;--bs-btn-bg: transparent;--bs-gradient: none}.btn-outline-dark{--bs-btn-color: #212529;--bs-btn-border-color: #212529;--bs-btn-hover-color: #ffffff;--bs-btn-hover-bg: #212529;--bs-btn-hover-border-color: #212529;--bs-btn-focus-shadow-rgb: 33, 37, 41;--bs-btn-active-color: #ffffff;--bs-btn-active-bg: #212529;--bs-btn-active-border-color: #212529;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #212529;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #212529;--bs-btn-bg: transparent;--bs-gradient: none}.btn-link{--bs-btn-font-weight: 400;--bs-btn-color: #0d6efd;--bs-btn-bg: transparent;--bs-btn-border-color: transparent;--bs-btn-hover-color: #0a58ca;--bs-btn-hover-border-color: transparent;--bs-btn-active-color: #0a58ca;--bs-btn-active-border-color: transparent;--bs-btn-disabled-color: #6c757d;--bs-btn-disabled-border-color: transparent;--bs-btn-box-shadow: 0 0 0 #000;--bs-btn-focus-shadow-rgb: 49, 132, 253;text-decoration:underline;-webkit-text-decoration:underline;-moz-text-decoration:underline;-ms-text-decoration:underline;-o-text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-lg,.btn-group-lg>.btn{--bs-btn-padding-y: 0.5rem;--bs-btn-padding-x: 1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius: 0.5rem}.btn-sm,.btn-group-sm>.btn{--bs-btn-padding-y: 0.25rem;--bs-btn-padding-x: 0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius: 0.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,.dropup-center,.dropdown-center{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 rgba(0,0,0,0);border-bottom:0;border-left:.3em solid rgba(0,0,0,0)}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex: 1000;--bs-dropdown-min-width: 10rem;--bs-dropdown-padding-x: 0;--bs-dropdown-padding-y: 0.5rem;--bs-dropdown-spacer: 0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color: #212529;--bs-dropdown-bg: #ffffff;--bs-dropdown-border-color: rgba(0, 0, 0, 0.175);--bs-dropdown-border-radius: 0.25rem;--bs-dropdown-border-width: 1px;--bs-dropdown-inner-border-radius: calc(0.25rem - 1px);--bs-dropdown-divider-bg: rgba(0, 0, 0, 0.175);--bs-dropdown-divider-margin-y: 0.5rem;--bs-dropdown-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-dropdown-link-color: #212529;--bs-dropdown-link-hover-color: #212529;--bs-dropdown-link-hover-bg: #f8f9fa;--bs-dropdown-link-active-color: #ffffff;--bs-dropdown-link-active-bg: #0d6efd;--bs-dropdown-link-disabled-color: rgba(33, 37, 41, 0.5);--bs-dropdown-item-padding-x: 1rem;--bs-dropdown-item-padding-y: 0.25rem;--bs-dropdown-header-color: #6c757d;--bs-dropdown-header-padding-x: 1rem;--bs-dropdown-header-padding-y: 0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.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:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid rgba(0,0,0,0);border-bottom:.3em solid;border-left:.3em solid rgba(0,0,0,0)}.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:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid rgba(0,0,0,0);border-right:0;border-bottom:.3em solid rgba(0,0,0,0);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:var(--bs-dropdown-spacer)}.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 rgba(0,0,0,0);border-right:.3em solid;border-bottom:.3em solid rgba(0,0,0,0)}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);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:rgba(0,0,0,0);border:0;border-radius:var(--bs-dropdown-item-border-radius, 0)}.dropdown-item:hover,.dropdown-item:focus{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:rgba(0,0,0,0)}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:0.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color: #dee2e6;--bs-dropdown-bg: #343a40;--bs-dropdown-border-color: rgba(0, 0, 0, 0.175);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color: #dee2e6;--bs-dropdown-link-hover-color: #ffffff;--bs-dropdown-divider-bg: rgba(0, 0, 0, 0.175);--bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color: #ffffff;--bs-dropdown-link-active-bg: #0d6efd;--bs-dropdown-link-disabled-color: #adb5bd;--bs-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{border-radius:.25rem}.btn-group>:not(.btn-check:first-child)+.btn,.btn-group>.btn-group:not(:first-child){margin-left:calc(1px*-1)}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn.dropdown-toggle-split:first-child,.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:calc(1px*-1)}.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{--bs-nav-link-padding-x: 1rem;--bs-nav-link-padding-y: 0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color: #0d6efd;--bs-nav-link-hover-color: #0a58ca;--bs-nav-link-disabled-color: rgba(33, 37, 41, 0.75);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:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;background:none;border:0;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:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.nav-link.disabled,.nav-link:disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width: 1px;--bs-nav-tabs-border-color: #dee2e6;--bs-nav-tabs-border-radius: 0.25rem;--bs-nav-tabs-link-hover-border-color: #e9ecef #e9ecef #dee2e6;--bs-nav-tabs-link-active-color: #000;--bs-nav-tabs-link-active-bg: #ffffff;--bs-nav-tabs-link-active-border-color: #dee2e6 #dee2e6 #ffffff;border-bottom:var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1*var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width) solid rgba(0,0,0,0);border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1*var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius: 0.25rem;--bs-nav-pills-link-active-color: #ffffff;--bs-nav-pills-link-active-bg: #0d6efd}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-underline{--bs-nav-underline-gap: 1rem;--bs-nav-underline-border-width: 0.125rem;--bs-nav-underline-link-active-color: #000;gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width) solid rgba(0,0,0,0)}.nav-underline .nav-link:hover,.nav-underline .nav-link:focus{border-bottom-color:currentcolor}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:currentcolor}.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{--bs-navbar-padding-x: 0;--bs-navbar-padding-y: 0.5rem;--bs-navbar-color: #fdfefe;--bs-navbar-hover-color: rgba(253, 254, 255, 0.8);--bs-navbar-disabled-color: rgba(253, 254, 254, 0.75);--bs-navbar-active-color: #fdfeff;--bs-navbar-brand-padding-y: 0.3125rem;--bs-navbar-brand-margin-end: 1rem;--bs-navbar-brand-font-size: 1.25rem;--bs-navbar-brand-color: #fdfefe;--bs-navbar-brand-hover-color: #fdfeff;--bs-navbar-nav-link-padding-x: 0.5rem;--bs-navbar-toggler-padding-y: 0.25;--bs-navbar-toggler-padding-x: 0;--bs-navbar-toggler-font-size: 1.25rem;--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23fdfefe' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color: rgba(253, 254, 254, 0);--bs-navbar-toggler-border-radius: 0.25rem;--bs-navbar-toggler-focus-width: 0.25rem;--bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out;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:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-sm,.navbar>.container-md,.navbar>.container-lg,.navbar>.container-xl,.navbar>.container-xxl{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:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x: 0;--bs-nav-link-padding-y: 0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color: var(--bs-navbar-color);--bs-nav-link-hover-color: var(--bs-navbar-hover-color);--bs-nav-link-disabled-color: var(--bs-navbar-disabled-color);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.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:hover,.navbar-text a:focus{color:var(--bs-navbar-active-color)}.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:var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:rgba(0,0,0,0);border:var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@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 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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{position:static;z-index:auto;flex-grow:1;-webkit-flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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{position:static;z-index:auto;flex-grow:1;-webkit-flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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{position:static;z-index:auto;flex-grow:1;-webkit-flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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{position:static;z-index:auto;flex-grow:1;-webkit-flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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{position:static;z-index:auto;flex-grow:1;-webkit-flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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{position:static;z-index:auto;flex-grow:1;-webkit-flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark]{--bs-navbar-color: #fdfefe;--bs-navbar-hover-color: rgba(253, 254, 255, 0.8);--bs-navbar-disabled-color: rgba(253, 254, 254, 0.75);--bs-navbar-active-color: #fdfeff;--bs-navbar-brand-color: #fdfefe;--bs-navbar-brand-hover-color: #fdfeff;--bs-navbar-toggler-border-color: rgba(253, 254, 254, 0);--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23fdfefe' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23fdfefe' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y: 1rem;--bs-card-spacer-x: 1rem;--bs-card-title-spacer-y: 0.5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width: 1px;--bs-card-border-color: rgba(0, 0, 0, 0.175);--bs-card-border-radius: 0.25rem;--bs-card-box-shadow: ;--bs-card-inner-border-radius: calc(0.25rem - 1px);--bs-card-cap-padding-y: 0.5rem;--bs-card-cap-padding-x: 1rem;--bs-card-cap-bg: rgba(52, 58, 64, 0.25);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg: #ffffff;--bs-card-img-overlay-padding: 1rem;--bs-card-group-margin: 0.75rem;position:relative;display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.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:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.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:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-0.5*var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0}.card-footer{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-0.5*var(--bs-card-cap-padding-x));margin-bottom:calc(-1*var(--bs-card-cap-padding-y));margin-left:calc(-0.5*var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-0.5*var(--bs-card-cap-padding-x));margin-left:calc(-0.5*var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-top,.card-img-bottom{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@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{--bs-accordion-color: #212529;--bs-accordion-bg: #ffffff;--bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease;--bs-accordion-border-color: #dee2e6;--bs-accordion-border-width: 1px;--bs-accordion-border-radius: 0.25rem;--bs-accordion-inner-border-radius: calc(0.25rem - 1px);--bs-accordion-btn-padding-x: 1.25rem;--bs-accordion-btn-padding-y: 1rem;--bs-accordion-btn-color: #212529;--bs-accordion-btn-bg: #ffffff;--bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%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");--bs-accordion-btn-icon-width: 1.25rem;--bs-accordion-btn-icon-transform: rotate(-180deg);--bs-accordion-btn-icon-transition: transform 0.2s ease-in-out;--bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23052c65'%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");--bs-accordion-btn-focus-border-color: #86b7fe;--bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-accordion-body-padding-x: 1.25rem;--bs-accordion-body-padding-y: 1rem;--bs-accordion-active-color: #052c65;--bs-accordion-active-bg: #cfe2ff}.accordion-button{position:relative;display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media(prefers-reduced-motion: reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1*var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;-webkit-flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media(prefers-reduced-motion: reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:var(--bs-accordion-btn-focus-border-color);outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x)}.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,.accordion-flush .accordion-item .accordion-button.collapsed{border-radius:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%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");--bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%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")}.breadcrumb{--bs-breadcrumb-padding-x: 0;--bs-breadcrumb-padding-y: 0;--bs-breadcrumb-margin-bottom: 1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color: rgba(33, 37, 41, 0.75);--bs-breadcrumb-item-padding-x: 0.5rem;--bs-breadcrumb-item-active-color: rgba(33, 37, 41, 0.75);display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider, ">") /* rtl: var(--bs-breadcrumb-divider, ">") */}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x: 0.75rem;--bs-pagination-padding-y: 0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color: #0d6efd;--bs-pagination-bg: #ffffff;--bs-pagination-border-width: 1px;--bs-pagination-border-color: #dee2e6;--bs-pagination-border-radius: 0.25rem;--bs-pagination-hover-color: #0a58ca;--bs-pagination-hover-bg: #f8f9fa;--bs-pagination-hover-border-color: #dee2e6;--bs-pagination-focus-color: #0a58ca;--bs-pagination-focus-bg: #e9ecef;--bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-pagination-active-color: #ffffff;--bs-pagination-active-bg: #0d6efd;--bs-pagination-active-border-color: #0d6efd;--bs-pagination-disabled-color: rgba(33, 37, 41, 0.75);--bs-pagination-disabled-bg: #e9ecef;--bs-pagination-disabled-border-color: #dee2e6;display:flex;display:-webkit-flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);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:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.page-link.active,.active>.page-link{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.page-link.disabled,.disabled>.page-link{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(1px*-1)}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x: 1.5rem;--bs-pagination-padding-y: 0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius: 0.5rem}.pagination-sm{--bs-pagination-padding-x: 0.5rem;--bs-pagination-padding-y: 0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius: 0.2em}.badge{--bs-badge-padding-x: 0.65em;--bs-badge-padding-y: 0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight: 700;--bs-badge-color: #ffffff;--bs-badge-border-radius: 0.25rem;display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg: transparent;--bs-alert-padding-x: 1rem;--bs-alert-padding-y: 1rem;--bs-alert-margin-bottom: 1rem;--bs-alert-color: inherit;--bs-alert-border-color: transparent;--bs-alert-border: 1px solid var(--bs-alert-border-color);--bs-alert-border-radius: 0.25rem;--bs-alert-link-color: inherit;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-default{--bs-alert-color: var(--bs-default-text-emphasis);--bs-alert-bg: var(--bs-default-bg-subtle);--bs-alert-border-color: var(--bs-default-border-subtle);--bs-alert-link-color: var(--bs-default-text-emphasis)}.alert-primary{--bs-alert-color: var(--bs-primary-text-emphasis);--bs-alert-bg: var(--bs-primary-bg-subtle);--bs-alert-border-color: var(--bs-primary-border-subtle);--bs-alert-link-color: var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color: var(--bs-secondary-text-emphasis);--bs-alert-bg: var(--bs-secondary-bg-subtle);--bs-alert-border-color: var(--bs-secondary-border-subtle);--bs-alert-link-color: var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color: var(--bs-success-text-emphasis);--bs-alert-bg: var(--bs-success-bg-subtle);--bs-alert-border-color: var(--bs-success-border-subtle);--bs-alert-link-color: var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color: var(--bs-info-text-emphasis);--bs-alert-bg: var(--bs-info-bg-subtle);--bs-alert-border-color: var(--bs-info-border-subtle);--bs-alert-link-color: var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color: var(--bs-warning-text-emphasis);--bs-alert-bg: var(--bs-warning-bg-subtle);--bs-alert-border-color: var(--bs-warning-border-subtle);--bs-alert-link-color: var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color: var(--bs-danger-text-emphasis);--bs-alert-bg: var(--bs-danger-bg-subtle);--bs-alert-border-color: var(--bs-danger-border-subtle);--bs-alert-link-color: var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color: var(--bs-light-text-emphasis);--bs-alert-bg: var(--bs-light-bg-subtle);--bs-alert-border-color: var(--bs-light-border-subtle);--bs-alert-link-color: var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color: var(--bs-dark-text-emphasis);--bs-alert-bg: var(--bs-dark-bg-subtle);--bs-alert-border-color: var(--bs-dark-border-subtle);--bs-alert-link-color: var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress,.progress-stacked{--bs-progress-height: 1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg: #e9ecef;--bs-progress-border-radius: 0.25rem;--bs-progress-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-progress-bar-color: #ffffff;--bs-progress-bar-bg: #0d6efd;--bs-progress-bar-transition: width 0.6s ease;display:flex;display:-webkit-flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.progress-bar{display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;justify-content:center;-webkit-justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@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:var(--bs-progress-height) var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media(prefers-reduced-motion: reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color: #212529;--bs-list-group-bg: #ffffff;--bs-list-group-border-color: #dee2e6;--bs-list-group-border-width: 1px;--bs-list-group-border-radius: 0.25rem;--bs-list-group-item-padding-x: 1rem;--bs-list-group-item-padding-y: 0.5rem;--bs-list-group-action-color: rgba(33, 37, 41, 0.75);--bs-list-group-action-hover-color: #000;--bs-list-group-action-hover-bg: #f8f9fa;--bs-list-group-action-active-color: #212529;--bs-list-group-action-active-bg: #e9ecef;--bs-list-group-disabled-color: rgba(33, 37, 41, 0.75);--bs-list-group-disabled-bg: #ffffff;--bs-list-group-active-color: #ffffff;--bs-list-group-active-bg: #0d6efd;--bs-list-group-active-border-color: #0d6efd;display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:hover,.list-group-item-action:focus{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color)}.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:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1*var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-horizontal{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);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:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media(min-width: 576px){.list-group-horizontal-sm{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);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:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width: 768px){.list-group-horizontal-md{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);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:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width: 992px){.list-group-horizontal-lg{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);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:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width: 1200px){.list-group-horizontal-xl{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);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:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width: 1400px){.list-group-horizontal-xxl{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);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:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-default{--bs-list-group-color: var(--bs-default-text-emphasis);--bs-list-group-bg: var(--bs-default-bg-subtle);--bs-list-group-border-color: var(--bs-default-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-default-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-default-border-subtle);--bs-list-group-active-color: var(--bs-default-bg-subtle);--bs-list-group-active-bg: var(--bs-default-text-emphasis);--bs-list-group-active-border-color: var(--bs-default-text-emphasis)}.list-group-item-primary{--bs-list-group-color: var(--bs-primary-text-emphasis);--bs-list-group-bg: var(--bs-primary-bg-subtle);--bs-list-group-border-color: var(--bs-primary-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-primary-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-primary-border-subtle);--bs-list-group-active-color: var(--bs-primary-bg-subtle);--bs-list-group-active-bg: var(--bs-primary-text-emphasis);--bs-list-group-active-border-color: var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color: var(--bs-secondary-text-emphasis);--bs-list-group-bg: var(--bs-secondary-bg-subtle);--bs-list-group-border-color: var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-secondary-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-secondary-border-subtle);--bs-list-group-active-color: var(--bs-secondary-bg-subtle);--bs-list-group-active-bg: var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color: var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color: var(--bs-success-text-emphasis);--bs-list-group-bg: var(--bs-success-bg-subtle);--bs-list-group-border-color: var(--bs-success-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-success-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-success-border-subtle);--bs-list-group-active-color: var(--bs-success-bg-subtle);--bs-list-group-active-bg: var(--bs-success-text-emphasis);--bs-list-group-active-border-color: var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color: var(--bs-info-text-emphasis);--bs-list-group-bg: var(--bs-info-bg-subtle);--bs-list-group-border-color: var(--bs-info-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-info-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-info-border-subtle);--bs-list-group-active-color: var(--bs-info-bg-subtle);--bs-list-group-active-bg: var(--bs-info-text-emphasis);--bs-list-group-active-border-color: var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color: var(--bs-warning-text-emphasis);--bs-list-group-bg: var(--bs-warning-bg-subtle);--bs-list-group-border-color: var(--bs-warning-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-warning-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-warning-border-subtle);--bs-list-group-active-color: var(--bs-warning-bg-subtle);--bs-list-group-active-bg: var(--bs-warning-text-emphasis);--bs-list-group-active-border-color: var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color: var(--bs-danger-text-emphasis);--bs-list-group-bg: var(--bs-danger-bg-subtle);--bs-list-group-border-color: var(--bs-danger-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-danger-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-danger-border-subtle);--bs-list-group-active-color: var(--bs-danger-bg-subtle);--bs-list-group-active-bg: var(--bs-danger-text-emphasis);--bs-list-group-active-border-color: var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color: var(--bs-light-text-emphasis);--bs-list-group-bg: var(--bs-light-bg-subtle);--bs-list-group-border-color: var(--bs-light-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-light-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-light-border-subtle);--bs-list-group-active-color: var(--bs-light-bg-subtle);--bs-list-group-active-bg: var(--bs-light-text-emphasis);--bs-list-group-active-border-color: var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color: var(--bs-dark-text-emphasis);--bs-list-group-bg: var(--bs-dark-bg-subtle);--bs-list-group-border-color: var(--bs-dark-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-dark-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-dark-border-subtle);--bs-list-group-active-color: var(--bs-dark-bg-subtle);--bs-list-group-active-bg: var(--bs-dark-text-emphasis);--bs-list-group-active-border-color: var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color: #000;--bs-btn-close-bg: 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 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");--bs-btn-close-opacity: 0.5;--bs-btn-close-hover-opacity: 0.75;--bs-btn-close-focus-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-btn-close-focus-opacity: 1;--bs-btn-close-disabled-opacity: 0.25;--bs-btn-close-white-filter: invert(1) grayscale(100%) brightness(200%);box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:var(--bs-btn-close-color);background:rgba(0,0,0,0) var(--bs-btn-close-bg) center/1em auto no-repeat;border:0;border-radius:.25rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.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:var(--bs-btn-close-disabled-opacity)}.btn-close-white{filter:var(--bs-btn-close-white-filter)}[data-bs-theme=dark] .btn-close{filter:var(--bs-btn-close-white-filter)}.toast{--bs-toast-zindex: 1090;--bs-toast-padding-x: 0.75rem;--bs-toast-padding-y: 0.5rem;--bs-toast-spacing: 1.5rem;--bs-toast-max-width: 350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg: rgba(255, 255, 255, 0.85);--bs-toast-border-width: 1px;--bs-toast-border-color: rgba(0, 0, 0, 0.175);--bs-toast-border-radius: 0.25rem;--bs-toast-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-toast-header-color: rgba(33, 37, 41, 0.75);--bs-toast-header-bg: rgba(255, 255, 255, 0.85);--bs-toast-header-border-color: rgba(0, 0, 0, 0.175);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width) solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex: 1090;position:absolute;z-index:var(--bs-toast-zindex);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:var(--bs-toast-spacing)}.toast-header{display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;padding:var(--bs-toast-padding-y) var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-0.5*var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex: 1055;--bs-modal-width: 500px;--bs-modal-padding: 1rem;--bs-modal-margin: 0.5rem;--bs-modal-color: ;--bs-modal-bg: #ffffff;--bs-modal-border-color: rgba(0, 0, 0, 0.175);--bs-modal-border-width: 1px;--bs-modal-border-radius: 0.5rem;--bs-modal-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-modal-inner-border-radius: calc(0.5rem - 1px);--bs-modal-header-padding-x: 1rem;--bs-modal-header-padding-y: 1rem;--bs-modal-header-padding: 1rem 1rem;--bs-modal-header-border-color: #dee2e6;--bs-modal-header-border-width: 1px;--bs-modal-title-line-height: 1.5;--bs-modal-footer-gap: 0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color: #dee2e6;--bs-modal-footer-border-width: 1px;position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);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% - var(--bs-modal-margin)*2)}.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% - var(--bs-modal-margin)*2)}.modal-content{position:relative;display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width) solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0}.modal-backdrop{--bs-backdrop-zindex: 1050;--bs-backdrop-bg: #000;--bs-backdrop-opacity: 0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.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:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y)*.5) calc(var(--bs-modal-header-padding-x)*.5);margin:calc(-0.5*var(--bs-modal-header-padding-y)) calc(-0.5*var(--bs-modal-header-padding-x)) calc(-0.5*var(--bs-modal-header-padding-y)) auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;-webkit-flex:1 1 auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;display:-webkit-flex;flex-shrink:0;-webkit-flex-shrink:0;flex-wrap:wrap;-webkit-flex-wrap:wrap;align-items:center;-webkit-align-items:center;justify-content:flex-end;-webkit-justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap)*.5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap)*.5)}@media(min-width: 576px){.modal{--bs-modal-margin: 1.75rem;--bs-modal-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width: 300px}}@media(min-width: 992px){.modal-lg,.modal-xl{--bs-modal-width: 800px}}@media(min-width: 1200px){.modal-xl{--bs-modal-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,.modal-fullscreen .modal-footer{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media(max-width: 575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header,.modal-fullscreen-sm-down .modal-footer{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media(max-width: 767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header,.modal-fullscreen-md-down .modal-footer{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media(max-width: 991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header,.modal-fullscreen-lg-down .modal-footer{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media(max-width: 1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header,.modal-fullscreen-xl-down .modal-footer{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media(max-width: 1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header,.modal-fullscreen-xxl-down .modal-footer{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex: 1080;--bs-tooltip-max-width: 200px;--bs-tooltip-padding-x: 0.5rem;--bs-tooltip-padding-y: 0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color: #ffffff;--bs-tooltip-bg: #000;--bs-tooltip-border-radius: 0.25rem;--bs-tooltip-opacity: 0.9;--bs-tooltip-arrow-width: 0.8rem;--bs-tooltip-arrow-height: 0.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";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;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:rgba(0,0,0,0);border-style:solid}.bs-tooltip-top .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow{bottom:calc(-1*var(--bs-tooltip-arrow-height))}.bs-tooltip-top .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width)*.5) 0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-end .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow{left:calc(-1*var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-end .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width)*.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width)*.5) 0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-bottom .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow{top:calc(-1*var(--bs-tooltip-arrow-height))}.bs-tooltip-bottom .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width)*.5) var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-start .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow{right:calc(-1*var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-start .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width)*.5) 0 calc(var(--bs-tooltip-arrow-width)*.5) var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex: 1070;--bs-popover-max-width: 276px;--bs-popover-font-size:0.875rem;--bs-popover-bg: #ffffff;--bs-popover-border-width: 1px;--bs-popover-border-color: rgba(0, 0, 0, 0.175);--bs-popover-border-radius: 0.5rem;--bs-popover-inner-border-radius: calc(0.5rem - 1px);--bs-popover-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-popover-header-padding-x: 1rem;--bs-popover-header-padding-y: 0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color: inherit;--bs-popover-header-bg: #e9ecef;--bs-popover-body-padding-x: 1rem;--bs-popover-body-padding-y: 1rem;--bs-popover-body-color: #212529;--bs-popover-arrow-width: 1rem;--bs-popover-arrow-height: 0.5rem;--bs-popover-arrow-border: var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";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;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::before,.popover .popover-arrow::after{position:absolute;display:block;content:"";border-color:rgba(0,0,0,0);border-style:solid;border-width:0}.bs-popover-top>.popover-arrow,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow{bottom:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-top>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after{border-width:var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width)*.5) 0}.bs-popover-top>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-top>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-end>.popover-arrow,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow{left:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-end>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after{border-width:calc(var(--bs-popover-arrow-width)*.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width)*.5) 0}.bs-popover-end>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-end>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-bottom>.popover-arrow,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow{top:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-bottom>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after{border-width:0 calc(var(--bs-popover-arrow-width)*.5) var(--bs-popover-arrow-height)}.bs-popover-bottom>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-bottom>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.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:var(--bs-popover-arrow-width);margin-left:calc(-0.5*var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-header-bg)}.bs-popover-start>.popover-arrow,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow{right:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-start>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after{border-width:calc(var(--bs-popover-arrow-width)*.5) 0 calc(var(--bs-popover-arrow-width)*.5) var(--bs-popover-arrow-height)}.bs-popover-start>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-start>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.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='%23ffffff'%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='%23ffffff'%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%}.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 rgba(0,0,0,0);border-bottom:10px solid rgba(0,0,0,0);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}[data-bs-theme=dark] .carousel .carousel-control-prev-icon,[data-bs-theme=dark] .carousel .carousel-control-next-icon,[data-bs-theme=dark].carousel .carousel-control-prev-icon,[data-bs-theme=dark].carousel .carousel-control-next-icon{filter:invert(1) grayscale(100)}[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target],[data-bs-theme=dark].carousel .carousel-indicators [data-bs-target]{background-color:#000}[data-bs-theme=dark] .carousel .carousel-caption,[data-bs-theme=dark].carousel .carousel-caption{color:#000}.spinner-grow,.spinner-border{display:inline-block;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg) /* rtl:ignore */}}.spinner-border{--bs-spinner-width: 2rem;--bs-spinner-height: 2rem;--bs-spinner-vertical-align: -0.125em;--bs-spinner-border-width: 0.25em;--bs-spinner-animation-speed: 0.75s;--bs-spinner-animation-name: spinner-border;border:var(--bs-spinner-border-width) solid currentcolor;border-right-color:rgba(0,0,0,0)}.spinner-border-sm{--bs-spinner-width: 1rem;--bs-spinner-height: 1rem;--bs-spinner-border-width: 0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width: 2rem;--bs-spinner-height: 2rem;--bs-spinner-vertical-align: -0.125em;--bs-spinner-animation-speed: 0.75s;--bs-spinner-animation-name: spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width: 1rem;--bs-spinner-height: 1rem}@media(prefers-reduced-motion: reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed: 1.5s}}.offcanvas,.offcanvas-xxl,.offcanvas-xl,.offcanvas-lg,.offcanvas-md,.offcanvas-sm{--bs-offcanvas-zindex: 1045;--bs-offcanvas-width: 400px;--bs-offcanvas-height: 30vh;--bs-offcanvas-padding-x: 1rem;--bs-offcanvas-padding-y: 1rem;--bs-offcanvas-color: #212529;--bs-offcanvas-bg: #ffffff;--bs-offcanvas-border-width: 1px;--bs-offcanvas-border-color: rgba(0, 0, 0, 0.175);--bs-offcanvas-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-offcanvas-transition: transform 0.3s ease-in-out;--bs-offcanvas-title-line-height: 1.5}@media(max-width: 575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 575.98px)and (prefers-reduced-motion: reduce){.offcanvas-sm{transition:none}}@media(max-width: 575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.showing,.offcanvas-sm.show:not(.hiding){transform:none}.offcanvas-sm.showing,.offcanvas-sm.hiding,.offcanvas-sm.show{visibility:visible}}@media(min-width: 576px){.offcanvas-sm{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}@media(max-width: 767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 767.98px)and (prefers-reduced-motion: reduce){.offcanvas-md{transition:none}}@media(max-width: 767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.showing,.offcanvas-md.show:not(.hiding){transform:none}.offcanvas-md.showing,.offcanvas-md.hiding,.offcanvas-md.show{visibility:visible}}@media(min-width: 768px){.offcanvas-md{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}@media(max-width: 991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 991.98px)and (prefers-reduced-motion: reduce){.offcanvas-lg{transition:none}}@media(max-width: 991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.showing,.offcanvas-lg.show:not(.hiding){transform:none}.offcanvas-lg.showing,.offcanvas-lg.hiding,.offcanvas-lg.show{visibility:visible}}@media(min-width: 992px){.offcanvas-lg{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}@media(max-width: 1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 1199.98px)and (prefers-reduced-motion: reduce){.offcanvas-xl{transition:none}}@media(max-width: 1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.showing,.offcanvas-xl.show:not(.hiding){transform:none}.offcanvas-xl.showing,.offcanvas-xl.hiding,.offcanvas-xl.show{visibility:visible}}@media(min-width: 1200px){.offcanvas-xl{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}@media(max-width: 1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 1399.98px)and (prefers-reduced-motion: reduce){.offcanvas-xxl{transition:none}}@media(max-width: 1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.showing,.offcanvas-xxl.show:not(.hiding){transform:none}.offcanvas-xxl.showing,.offcanvas-xxl.hiding,.offcanvas-xxl.show{visibility:visible}}@media(min-width: 1400px){.offcanvas-xxl{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;display:-webkit-flex;flex-grow:0;-webkit-flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media(prefers-reduced-motion: reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.showing,.offcanvas.show:not(.hiding){transform:none}.offcanvas.showing,.offcanvas.hiding,.offcanvas.show{visibility:visible}.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:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y)*.5) calc(var(--bs-offcanvas-padding-x)*.5);margin-top:calc(-0.5*var(--bs-offcanvas-padding-y));margin-right:calc(-0.5*var(--bs-offcanvas-padding-x));margin-bottom:calc(-0.5*var(--bs-offcanvas-padding-y))}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;-webkit-flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto}.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:""}.text-bg-default{color:#000 !important;background-color:RGBA(var(--bs-default-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-primary{color:#fff !important;background-color:RGBA(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-secondary{color:#fff !important;background-color:RGBA(var(--bs-secondary-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-success{color:#fff !important;background-color:RGBA(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-info{color:#000 !important;background-color:RGBA(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-warning{color:#000 !important;background-color:RGBA(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-danger{color:#fff !important;background-color:RGBA(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-light{color:#000 !important;background-color:RGBA(var(--bs-light-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-dark{color:#fff !important;background-color:RGBA(var(--bs-dark-rgb), var(--bs-bg-opacity, 1)) !important}.link-default{color:RGBA(var(--bs-default-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-default-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-default:hover,.link-default:focus{color:RGBA(229, 232, 235, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(229, 232, 235, var(--bs-link-underline-opacity, 1)) !important}.link-primary{color:RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-primary:hover,.link-primary:focus{color:RGBA(10, 88, 202, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important}.link-secondary{color:RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-secondary:hover,.link-secondary:focus{color:RGBA(86, 94, 100, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important}.link-success{color:RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-success:hover,.link-success:focus{color:RGBA(20, 108, 67, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important}.link-info{color:RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-info:hover,.link-info:focus{color:RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important}.link-warning{color:RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-warning:hover,.link-warning:focus{color:RGBA(255, 205, 57, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important}.link-danger{color:RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-danger:hover,.link-danger:focus{color:RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important}.link-light{color:RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-light:hover,.link-light:focus{color:RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important}.link-dark{color:RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-dark:hover,.link-dark:focus{color:RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-body-emphasis:hover,.link-body-emphasis:focus{color:RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75)) !important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;-webkit-align-items:center;text-decoration-color:rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5));text-underline-offset:.25em;backface-visibility:hidden;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;-o-backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;-webkit-flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media(prefers-reduced-motion: reduce){.icon-link>.bi{transition:none}}.icon-link-hover:hover>.bi,.icon-link-hover:focus-visible>.bi{transform:var(--bs-icon-link-transform, translate3d(0.25em, 0, 0))}.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: 75%}.ratio-16x9{--bs-aspect-ratio: 56.25%}.ratio-21x9{--bs-aspect-ratio: 42.8571428571%}.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}.sticky-bottom{position:sticky;bottom:0;z-index:1020}@media(min-width: 576px){.sticky-sm-top{position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width: 768px){.sticky-md-top{position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width: 992px){.sticky-lg-top{position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width: 1200px){.sticky-xl-top{position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width: 1400px){.sticky-xxl-top{position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:sticky;bottom: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){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}.visually-hidden:not(caption),.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption){position:absolute !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}.object-fit-contain{object-fit:contain !important}.object-fit-cover{object-fit:cover !important}.object-fit-fill{object-fit:fill !important}.object-fit-scale{object-fit:scale-down !important}.object-fit-none{object-fit: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}.overflow-x-auto{overflow-x:auto !important}.overflow-x-hidden{overflow-x:hidden !important}.overflow-x-visible{overflow-x:visible !important}.overflow-x-scroll{overflow-x:scroll !important}.overflow-y-auto{overflow-y:auto !important}.overflow-y-hidden{overflow-y:hidden !important}.overflow-y-visible{overflow-y:visible !important}.overflow-y-scroll{overflow-y: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-inline-grid{display:inline-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}.focus-ring-default{--bs-focus-ring-color: rgba(var(--bs-default-rgb), var(--bs-focus-ring-opacity))}.focus-ring-primary{--bs-focus-ring-color: rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color: rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color: rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color: rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color: rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color: rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.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:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-0{border:0 !important}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-top-0{border-top:0 !important}.border-end{border-right:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-end-0{border-right:0 !important}.border-bottom{border-bottom:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-bottom-0{border-bottom:0 !important}.border-start{border-left:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-start-0{border-left:0 !important}.border-default{--bs-border-opacity: 1;border-color:rgba(var(--bs-default-rgb), var(--bs-border-opacity)) !important}.border-primary{--bs-border-opacity: 1;border-color:rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important}.border-secondary{--bs-border-opacity: 1;border-color:rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important}.border-success{--bs-border-opacity: 1;border-color:rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important}.border-info{--bs-border-opacity: 1;border-color:rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important}.border-warning{--bs-border-opacity: 1;border-color:rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important}.border-danger{--bs-border-opacity: 1;border-color:rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important}.border-light{--bs-border-opacity: 1;border-color:rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important}.border-dark{--bs-border-opacity: 1;border-color:rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important}.border-black{--bs-border-opacity: 1;border-color:rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important}.border-white{--bs-border-opacity: 1;border-color:rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle) !important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle) !important}.border-success-subtle{border-color:var(--bs-success-border-subtle) !important}.border-info-subtle{border-color:var(--bs-info-border-subtle) !important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle) !important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle) !important}.border-light-subtle{border-color:var(--bs-light-border-subtle) !important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle) !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}.border-opacity-10{--bs-border-opacity: 0.1}.border-opacity-25{--bs-border-opacity: 0.25}.border-opacity-50{--bs-border-opacity: 0.5}.border-opacity-75{--bs-border-opacity: 0.75}.border-opacity-100{--bs-border-opacity: 1}.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}.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}.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}.row-gap-0{row-gap:0 !important}.row-gap-1{row-gap:.25rem !important}.row-gap-2{row-gap:.5rem !important}.row-gap-3{row-gap:1rem !important}.row-gap-4{row-gap:1.5rem !important}.row-gap-5{row-gap:3rem !important}.column-gap-0{column-gap:0 !important}.column-gap-1{column-gap:.25rem !important}.column-gap-2{column-gap:.5rem !important}.column-gap-3{column-gap:1rem !important}.column-gap-4{column-gap:1.5rem !important}.column-gap-5{column-gap:3rem !important}.font-monospace{font-family:var(--bs-font-monospace) !important}.fs-1{font-size:calc(1.325rem + 0.9vw) !important}.fs-2{font-size:calc(1.29rem + 0.48vw) !important}.fs-3{font-size:calc(1.27rem + 0.24vw) !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-lighter{font-weight:lighter !important}.fw-light{font-weight:300 !important}.fw-normal{font-weight:400 !important}.fw-medium{font-weight:500 !important}.fw-semibold{font-weight:600 !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:var(--bs-secondary-color) !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-body-secondary{--bs-text-opacity: 1;color:var(--bs-secondary-color) !important}.text-body-tertiary{--bs-text-opacity: 1;color:var(--bs-tertiary-color) !important}.text-body-emphasis{--bs-text-opacity: 1;color:var(--bs-emphasis-color) !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}.text-primary-emphasis{color:var(--bs-primary-text-emphasis) !important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis) !important}.text-success-emphasis{color:var(--bs-success-text-emphasis) !important}.text-info-emphasis{color:var(--bs-info-text-emphasis) !important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis) !important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis) !important}.text-light-emphasis{color:var(--bs-light-text-emphasis) !important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis) !important}.link-opacity-10{--bs-link-opacity: 0.1}.link-opacity-10-hover:hover{--bs-link-opacity: 0.1}.link-opacity-25{--bs-link-opacity: 0.25}.link-opacity-25-hover:hover{--bs-link-opacity: 0.25}.link-opacity-50{--bs-link-opacity: 0.5}.link-opacity-50-hover:hover{--bs-link-opacity: 0.5}.link-opacity-75{--bs-link-opacity: 0.75}.link-opacity-75-hover:hover{--bs-link-opacity: 0.75}.link-opacity-100{--bs-link-opacity: 1}.link-opacity-100-hover:hover{--bs-link-opacity: 1}.link-offset-1{text-underline-offset:.125em !important}.link-offset-1-hover:hover{text-underline-offset:.125em !important}.link-offset-2{text-underline-offset:.25em !important}.link-offset-2-hover:hover{text-underline-offset:.25em !important}.link-offset-3{text-underline-offset:.375em !important}.link-offset-3-hover:hover{text-underline-offset:.375em !important}.link-underline-default{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-default-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-primary{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-secondary{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-success{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-info{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-warning{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-danger{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-light{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-dark{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important}.link-underline{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-underline-opacity-0{--bs-link-underline-opacity: 0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity: 0}.link-underline-opacity-10{--bs-link-underline-opacity: 0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity: 0.1}.link-underline-opacity-25{--bs-link-underline-opacity: 0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity: 0.25}.link-underline-opacity-50{--bs-link-underline-opacity: 0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity: 0.5}.link-underline-opacity-75{--bs-link-underline-opacity: 0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity: 0.75}.link-underline-opacity-100{--bs-link-underline-opacity: 1}.link-underline-opacity-100-hover:hover{--bs-link-underline-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:rgba(0,0,0,0) !important}.bg-body-secondary{--bs-bg-opacity: 1;background-color:rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important}.bg-body-tertiary{--bs-bg-opacity: 1;background-color:rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !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-primary-subtle{background-color:var(--bs-primary-bg-subtle) !important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle) !important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle) !important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle) !important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle) !important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle) !important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle) !important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle) !important}.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:var(--bs-border-radius) !important}.rounded-0{border-radius:0 !important}.rounded-1{border-radius:var(--bs-border-radius-sm) !important}.rounded-2{border-radius:var(--bs-border-radius) !important}.rounded-3{border-radius:var(--bs-border-radius-lg) !important}.rounded-4{border-radius:var(--bs-border-radius-xl) !important}.rounded-5{border-radius:var(--bs-border-radius-xxl) !important}.rounded-circle{border-radius:50% !important}.rounded-pill{border-radius:var(--bs-border-radius-pill) !important}.rounded-top{border-top-left-radius:var(--bs-border-radius) !important;border-top-right-radius:var(--bs-border-radius) !important}.rounded-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm) !important;border-top-right-radius:var(--bs-border-radius-sm) !important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius) !important;border-top-right-radius:var(--bs-border-radius) !important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg) !important;border-top-right-radius:var(--bs-border-radius-lg) !important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl) !important;border-top-right-radius:var(--bs-border-radius-xl) !important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl) !important;border-top-right-radius:var(--bs-border-radius-xxl) !important}.rounded-top-circle{border-top-left-radius:50% !important;border-top-right-radius:50% !important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill) !important;border-top-right-radius:var(--bs-border-radius-pill) !important}.rounded-end{border-top-right-radius:var(--bs-border-radius) !important;border-bottom-right-radius:var(--bs-border-radius) !important}.rounded-end-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm) !important;border-bottom-right-radius:var(--bs-border-radius-sm) !important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius) !important;border-bottom-right-radius:var(--bs-border-radius) !important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg) !important;border-bottom-right-radius:var(--bs-border-radius-lg) !important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl) !important;border-bottom-right-radius:var(--bs-border-radius-xl) !important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl) !important;border-bottom-right-radius:var(--bs-border-radius-xxl) !important}.rounded-end-circle{border-top-right-radius:50% !important;border-bottom-right-radius:50% !important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill) !important;border-bottom-right-radius:var(--bs-border-radius-pill) !important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius) !important;border-bottom-left-radius:var(--bs-border-radius) !important}.rounded-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm) !important;border-bottom-left-radius:var(--bs-border-radius-sm) !important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius) !important;border-bottom-left-radius:var(--bs-border-radius) !important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg) !important;border-bottom-left-radius:var(--bs-border-radius-lg) !important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl) !important;border-bottom-left-radius:var(--bs-border-radius-xl) !important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl) !important;border-bottom-left-radius:var(--bs-border-radius-xxl) !important}.rounded-bottom-circle{border-bottom-right-radius:50% !important;border-bottom-left-radius:50% !important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill) !important;border-bottom-left-radius:var(--bs-border-radius-pill) !important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius) !important;border-top-left-radius:var(--bs-border-radius) !important}.rounded-start-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm) !important;border-top-left-radius:var(--bs-border-radius-sm) !important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius) !important;border-top-left-radius:var(--bs-border-radius) !important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg) !important;border-top-left-radius:var(--bs-border-radius-lg) !important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl) !important;border-top-left-radius:var(--bs-border-radius-xl) !important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl) !important;border-top-left-radius:var(--bs-border-radius-xxl) !important}.rounded-start-circle{border-bottom-left-radius:50% !important;border-top-left-radius:50% !important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill) !important;border-top-left-radius:var(--bs-border-radius-pill) !important}.visible{visibility:visible !important}.invisible{visibility:hidden !important}.z-n1{z-index:-1 !important}.z-0{z-index:0 !important}.z-1{z-index:1 !important}.z-2{z-index:2 !important}.z-3{z-index:3 !important}@media(min-width: 576px){.float-sm-start{float:left !important}.float-sm-end{float:right !important}.float-sm-none{float:none !important}.object-fit-sm-contain{object-fit:contain !important}.object-fit-sm-cover{object-fit:cover !important}.object-fit-sm-fill{object-fit:fill !important}.object-fit-sm-scale{object-fit:scale-down !important}.object-fit-sm-none{object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-sm-0{row-gap:0 !important}.row-gap-sm-1{row-gap:.25rem !important}.row-gap-sm-2{row-gap:.5rem !important}.row-gap-sm-3{row-gap:1rem !important}.row-gap-sm-4{row-gap:1.5rem !important}.row-gap-sm-5{row-gap:3rem !important}.column-gap-sm-0{column-gap:0 !important}.column-gap-sm-1{column-gap:.25rem !important}.column-gap-sm-2{column-gap:.5rem !important}.column-gap-sm-3{column-gap:1rem !important}.column-gap-sm-4{column-gap:1.5rem !important}.column-gap-sm-5{column-gap: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}.object-fit-md-contain{object-fit:contain !important}.object-fit-md-cover{object-fit:cover !important}.object-fit-md-fill{object-fit:fill !important}.object-fit-md-scale{object-fit:scale-down !important}.object-fit-md-none{object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-md-0{row-gap:0 !important}.row-gap-md-1{row-gap:.25rem !important}.row-gap-md-2{row-gap:.5rem !important}.row-gap-md-3{row-gap:1rem !important}.row-gap-md-4{row-gap:1.5rem !important}.row-gap-md-5{row-gap:3rem !important}.column-gap-md-0{column-gap:0 !important}.column-gap-md-1{column-gap:.25rem !important}.column-gap-md-2{column-gap:.5rem !important}.column-gap-md-3{column-gap:1rem !important}.column-gap-md-4{column-gap:1.5rem !important}.column-gap-md-5{column-gap: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}.object-fit-lg-contain{object-fit:contain !important}.object-fit-lg-cover{object-fit:cover !important}.object-fit-lg-fill{object-fit:fill !important}.object-fit-lg-scale{object-fit:scale-down !important}.object-fit-lg-none{object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-lg-0{row-gap:0 !important}.row-gap-lg-1{row-gap:.25rem !important}.row-gap-lg-2{row-gap:.5rem !important}.row-gap-lg-3{row-gap:1rem !important}.row-gap-lg-4{row-gap:1.5rem !important}.row-gap-lg-5{row-gap:3rem !important}.column-gap-lg-0{column-gap:0 !important}.column-gap-lg-1{column-gap:.25rem !important}.column-gap-lg-2{column-gap:.5rem !important}.column-gap-lg-3{column-gap:1rem !important}.column-gap-lg-4{column-gap:1.5rem !important}.column-gap-lg-5{column-gap: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}.object-fit-xl-contain{object-fit:contain !important}.object-fit-xl-cover{object-fit:cover !important}.object-fit-xl-fill{object-fit:fill !important}.object-fit-xl-scale{object-fit:scale-down !important}.object-fit-xl-none{object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-xl-0{row-gap:0 !important}.row-gap-xl-1{row-gap:.25rem !important}.row-gap-xl-2{row-gap:.5rem !important}.row-gap-xl-3{row-gap:1rem !important}.row-gap-xl-4{row-gap:1.5rem !important}.row-gap-xl-5{row-gap:3rem !important}.column-gap-xl-0{column-gap:0 !important}.column-gap-xl-1{column-gap:.25rem !important}.column-gap-xl-2{column-gap:.5rem !important}.column-gap-xl-3{column-gap:1rem !important}.column-gap-xl-4{column-gap:1.5rem !important}.column-gap-xl-5{column-gap: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}.object-fit-xxl-contain{object-fit:contain !important}.object-fit-xxl-cover{object-fit:cover !important}.object-fit-xxl-fill{object-fit:fill !important}.object-fit-xxl-scale{object-fit:scale-down !important}.object-fit-xxl-none{object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-xxl-0{row-gap:0 !important}.row-gap-xxl-1{row-gap:.25rem !important}.row-gap-xxl-2{row-gap:.5rem !important}.row-gap-xxl-3{row-gap:1rem !important}.row-gap-xxl-4{row-gap:1.5rem !important}.row-gap-xxl-5{row-gap:3rem !important}.column-gap-xxl-0{column-gap:0 !important}.column-gap-xxl-1{column-gap:.25rem !important}.column-gap-xxl-2{column-gap:.5rem !important}.column-gap-xxl-3{column-gap:1rem !important}.column-gap-xxl-4{column-gap:1.5rem !important}.column-gap-xxl-5{column-gap: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:#fff}.bg-success{color:#fff}.bg-info{color:#000}.bg-warning{color:#000}.bg-danger{color:#fff}.bg-light{color:#000}.bg-dark{color:#fff}@media(min-width: 1200px){.fs-1{font-size:2rem !important}.fs-2{font-size:1.65rem !important}.fs-3{font-size:1.45rem !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-inline-grid{display:inline-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}}:root{--bslib-spacer: 1rem;--bslib-mb-spacer: var(--bslib-spacer, 1rem)}.bslib-mb-spacing{margin-bottom:var(--bslib-mb-spacer)}.bslib-gap-spacing{gap:var(--bslib-mb-spacer)}.bslib-gap-spacing>.bslib-mb-spacing,.bslib-gap-spacing>.form-group,.bslib-gap-spacing>p,.bslib-gap-spacing>pre{margin-bottom:0}.html-fill-container>.html-fill-item.bslib-mb-spacing{margin-bottom:0}.tab-content>.tab-pane.html-fill-container{display:none}.tab-content>.active.html-fill-container{display:flex}.tab-content.html-fill-container{padding:0}.bg-blue{--bslib-color-bg: #0d6efd;--bslib-color-fg: #ffffff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-blue{--bslib-color-fg: #0d6efd;color:var(--bslib-color-fg)}.bg-indigo{--bslib-color-bg: #6610f2;--bslib-color-fg: #ffffff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-indigo{--bslib-color-fg: #6610f2;color:var(--bslib-color-fg)}.bg-purple{--bslib-color-bg: #6f42c1;--bslib-color-fg: #ffffff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-purple{--bslib-color-fg: #6f42c1;color:var(--bslib-color-fg)}.bg-pink{--bslib-color-bg: #d63384;--bslib-color-fg: #ffffff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-pink{--bslib-color-fg: #d63384;color:var(--bslib-color-fg)}.bg-red{--bslib-color-bg: #dc3545;--bslib-color-fg: #ffffff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-red{--bslib-color-fg: #dc3545;color:var(--bslib-color-fg)}.bg-orange{--bslib-color-bg: #fd7e14;--bslib-color-fg: #000;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-orange{--bslib-color-fg: #fd7e14;color:var(--bslib-color-fg)}.bg-yellow{--bslib-color-bg: #ffc107;--bslib-color-fg: #000;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-yellow{--bslib-color-fg: #ffc107;color:var(--bslib-color-fg)}.bg-green{--bslib-color-bg: #198754;--bslib-color-fg: #ffffff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-green{--bslib-color-fg: #198754;color:var(--bslib-color-fg)}.bg-teal{--bslib-color-bg: #20c997;--bslib-color-fg: #000;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-teal{--bslib-color-fg: #20c997;color:var(--bslib-color-fg)}.bg-cyan{--bslib-color-bg: #0dcaf0;--bslib-color-fg: #000;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-cyan{--bslib-color-fg: #0dcaf0;color:var(--bslib-color-fg)}.text-default{--bslib-color-fg: #dee2e6}.bg-default{--bslib-color-bg: #dee2e6;--bslib-color-fg: #000}.text-primary{--bslib-color-fg: #0d6efd}.bg-primary{--bslib-color-bg: #0d6efd;--bslib-color-fg: #ffffff}.text-secondary{--bslib-color-fg: #6c757d}.bg-secondary{--bslib-color-bg: #6c757d;--bslib-color-fg: #ffffff}.text-success{--bslib-color-fg: #198754}.bg-success{--bslib-color-bg: #198754;--bslib-color-fg: #ffffff}.text-info{--bslib-color-fg: #0dcaf0}.bg-info{--bslib-color-bg: #0dcaf0;--bslib-color-fg: #000}.text-warning{--bslib-color-fg: #ffc107}.bg-warning{--bslib-color-bg: #ffc107;--bslib-color-fg: #000}.text-danger{--bslib-color-fg: #dc3545}.bg-danger{--bslib-color-bg: #dc3545;--bslib-color-fg: #ffffff}.text-light{--bslib-color-fg: #f8f9fa}.bg-light{--bslib-color-bg: #f8f9fa;--bslib-color-fg: #000}.text-dark{--bslib-color-fg: #212529}.bg-dark{--bslib-color-bg: #212529;--bslib-color-fg: #ffffff}.bg-gradient-blue-indigo{--bslib-color-fg: #ffffff;--bslib-color-bg: #3148f9;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #3148f9;color:#fff}.bg-gradient-blue-purple{--bslib-color-fg: #ffffff;--bslib-color-bg: #345ce5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #345ce5;color:#fff}.bg-gradient-blue-pink{--bslib-color-fg: #ffffff;--bslib-color-bg: #5d56cd;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #5d56cd;color:#fff}.bg-gradient-blue-red{--bslib-color-fg: #ffffff;--bslib-color-bg: #6057b3;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #6057b3;color:#fff}.bg-gradient-blue-orange{--bslib-color-fg: #ffffff;--bslib-color-bg: #6d74a0;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #6d74a0;color:#fff}.bg-gradient-blue-yellow{--bslib-color-fg: #000;--bslib-color-bg: #6e8f9b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #6e8f9b;color:#000}.bg-gradient-blue-green{--bslib-color-fg: #ffffff;--bslib-color-bg: #1278b9;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #1278b9;color:#fff}.bg-gradient-blue-teal{--bslib-color-fg: #000;--bslib-color-bg: #1592d4;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #1592d4;color:#000}.bg-gradient-blue-cyan{--bslib-color-fg: #000;--bslib-color-bg: #0d93f8;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #0d93f8;color:#000}.bg-gradient-indigo-blue{--bslib-color-fg: #ffffff;--bslib-color-bg: #4236f6;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #4236f6;color:#fff}.bg-gradient-indigo-purple{--bslib-color-fg: #ffffff;--bslib-color-bg: #6a24de;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #6a24de;color:#fff}.bg-gradient-indigo-pink{--bslib-color-fg: #ffffff;--bslib-color-bg: #931ec6;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #931ec6;color:#fff}.bg-gradient-indigo-red{--bslib-color-fg: #ffffff;--bslib-color-bg: #951fad;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #951fad;color:#fff}.bg-gradient-indigo-orange{--bslib-color-fg: #ffffff;--bslib-color-bg: #a23c99;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #a23c99;color:#fff}.bg-gradient-indigo-yellow{--bslib-color-fg: #ffffff;--bslib-color-bg: #a35794;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #a35794;color:#fff}.bg-gradient-indigo-green{--bslib-color-fg: #ffffff;--bslib-color-bg: #4740b3;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #4740b3;color:#fff}.bg-gradient-indigo-teal{--bslib-color-fg: #ffffff;--bslib-color-bg: #4a5ace;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #4a5ace;color:#fff}.bg-gradient-indigo-cyan{--bslib-color-fg: #ffffff;--bslib-color-bg: #425af1;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #425af1;color:#fff}.bg-gradient-purple-blue{--bslib-color-fg: #ffffff;--bslib-color-bg: #4854d9;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #4854d9;color:#fff}.bg-gradient-purple-indigo{--bslib-color-fg: #ffffff;--bslib-color-bg: #6b2ed5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #6b2ed5;color:#fff}.bg-gradient-purple-pink{--bslib-color-fg: #ffffff;--bslib-color-bg: #983ca9;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #983ca9;color:#fff}.bg-gradient-purple-red{--bslib-color-fg: #ffffff;--bslib-color-bg: #9b3d8f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #9b3d8f;color:#fff}.bg-gradient-purple-orange{--bslib-color-fg: #ffffff;--bslib-color-bg: #a85a7c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #a85a7c;color:#fff}.bg-gradient-purple-yellow{--bslib-color-fg: #000;--bslib-color-bg: #a97577;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #a97577;color:#000}.bg-gradient-purple-green{--bslib-color-fg: #ffffff;--bslib-color-bg: #4d5e95;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #4d5e95;color:#fff}.bg-gradient-purple-teal{--bslib-color-fg: #ffffff;--bslib-color-bg: #4f78b0;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #4f78b0;color:#fff}.bg-gradient-purple-cyan{--bslib-color-fg: #000;--bslib-color-bg: #4878d4;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #4878d4;color:#000}.bg-gradient-pink-blue{--bslib-color-fg: #ffffff;--bslib-color-bg: #864bb4;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #864bb4;color:#fff}.bg-gradient-pink-indigo{--bslib-color-fg: #ffffff;--bslib-color-bg: #a925b0;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #a925b0;color:#fff}.bg-gradient-pink-purple{--bslib-color-fg: #ffffff;--bslib-color-bg: #ad399c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #ad399c;color:#fff}.bg-gradient-pink-red{--bslib-color-fg: #ffffff;--bslib-color-bg: #d8346b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #d8346b;color:#fff}.bg-gradient-pink-orange{--bslib-color-fg: #000;--bslib-color-bg: #e65157;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #e65157;color:#000}.bg-gradient-pink-yellow{--bslib-color-fg: #000;--bslib-color-bg: #e66c52;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #e66c52;color:#000}.bg-gradient-pink-green{--bslib-color-fg: #ffffff;--bslib-color-bg: #8a5571;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #8a5571;color:#fff}.bg-gradient-pink-teal{--bslib-color-fg: #000;--bslib-color-bg: #8d6f8c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #8d6f8c;color:#000}.bg-gradient-pink-cyan{--bslib-color-fg: #000;--bslib-color-bg: #866faf;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #866faf;color:#000}.bg-gradient-red-blue{--bslib-color-fg: #ffffff;--bslib-color-bg: #894c8f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #894c8f;color:#fff}.bg-gradient-red-indigo{--bslib-color-fg: #ffffff;--bslib-color-bg: #ad268a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #ad268a;color:#fff}.bg-gradient-red-purple{--bslib-color-fg: #ffffff;--bslib-color-bg: #b03a77;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #b03a77;color:#fff}.bg-gradient-red-pink{--bslib-color-fg: #ffffff;--bslib-color-bg: #da345e;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #da345e;color:#fff}.bg-gradient-red-orange{--bslib-color-fg: #000;--bslib-color-bg: #e95231;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #e95231;color:#000}.bg-gradient-red-yellow{--bslib-color-fg: #000;--bslib-color-bg: #ea6d2c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #ea6d2c;color:#000}.bg-gradient-red-green{--bslib-color-fg: #ffffff;--bslib-color-bg: #8e564b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #8e564b;color:#fff}.bg-gradient-red-teal{--bslib-color-fg: #000;--bslib-color-bg: #917066;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #917066;color:#000}.bg-gradient-red-cyan{--bslib-color-fg: #000;--bslib-color-bg: #897189;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #897189;color:#000}.bg-gradient-orange-blue{--bslib-color-fg: #000;--bslib-color-bg: #9d7871;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #9d7871;color:#000}.bg-gradient-orange-indigo{--bslib-color-fg: #000;--bslib-color-bg: #c1526d;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #c1526d;color:#000}.bg-gradient-orange-purple{--bslib-color-fg: #000;--bslib-color-bg: #c46659;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #c46659;color:#000}.bg-gradient-orange-pink{--bslib-color-fg: #000;--bslib-color-bg: #ed6041;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #ed6041;color:#000}.bg-gradient-orange-red{--bslib-color-fg: #000;--bslib-color-bg: #f06128;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #f06128;color:#000}.bg-gradient-orange-yellow{--bslib-color-fg: #000;--bslib-color-bg: #fe990f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #fe990f;color:#000}.bg-gradient-orange-green{--bslib-color-fg: #000;--bslib-color-bg: #a2822e;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #a2822e;color:#000}.bg-gradient-orange-teal{--bslib-color-fg: #000;--bslib-color-bg: #a59c48;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #a59c48;color:#000}.bg-gradient-orange-cyan{--bslib-color-fg: #000;--bslib-color-bg: #9d9c6c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #9d9c6c;color:#000}.bg-gradient-yellow-blue{--bslib-color-fg: #000;--bslib-color-bg: #9ea069;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #9ea069;color:#000}.bg-gradient-yellow-indigo{--bslib-color-fg: #000;--bslib-color-bg: #c27a65;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #c27a65;color:#000}.bg-gradient-yellow-purple{--bslib-color-fg: #000;--bslib-color-bg: #c58e51;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #c58e51;color:#000}.bg-gradient-yellow-pink{--bslib-color-fg: #000;--bslib-color-bg: #ef8839;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #ef8839;color:#000}.bg-gradient-yellow-red{--bslib-color-fg: #000;--bslib-color-bg: #f18920;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #f18920;color:#000}.bg-gradient-yellow-orange{--bslib-color-fg: #000;--bslib-color-bg: #fea60c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #fea60c;color:#000}.bg-gradient-yellow-green{--bslib-color-fg: #000;--bslib-color-bg: #a3aa26;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #a3aa26;color:#000}.bg-gradient-yellow-teal{--bslib-color-fg: #000;--bslib-color-bg: #a6c441;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #a6c441;color:#000}.bg-gradient-yellow-cyan{--bslib-color-fg: #000;--bslib-color-bg: #9ec564;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #9ec564;color:#000}.bg-gradient-green-blue{--bslib-color-fg: #ffffff;--bslib-color-bg: #147d98;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #147d98;color:#fff}.bg-gradient-green-indigo{--bslib-color-fg: #ffffff;--bslib-color-bg: #385793;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #385793;color:#fff}.bg-gradient-green-purple{--bslib-color-fg: #ffffff;--bslib-color-bg: #3b6b80;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #3b6b80;color:#fff}.bg-gradient-green-pink{--bslib-color-fg: #ffffff;--bslib-color-bg: #656567;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #656567;color:#fff}.bg-gradient-green-red{--bslib-color-fg: #ffffff;--bslib-color-bg: #67664e;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #67664e;color:#fff}.bg-gradient-green-orange{--bslib-color-fg: #000;--bslib-color-bg: #74833a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #74833a;color:#000}.bg-gradient-green-yellow{--bslib-color-fg: #000;--bslib-color-bg: #759e35;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #759e35;color:#000}.bg-gradient-green-teal{--bslib-color-fg: #000;--bslib-color-bg: #1ca16f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #1ca16f;color:#000}.bg-gradient-green-cyan{--bslib-color-fg: #000;--bslib-color-bg: #14a292;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #14a292;color:#000}.bg-gradient-teal-blue{--bslib-color-fg: #000;--bslib-color-bg: #18a5c0;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #18a5c0;color:#000}.bg-gradient-teal-indigo{--bslib-color-fg: #000;--bslib-color-bg: #3c7fbb;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #3c7fbb;color:#000}.bg-gradient-teal-purple{--bslib-color-fg: #000;--bslib-color-bg: #4093a8;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #4093a8;color:#000}.bg-gradient-teal-pink{--bslib-color-fg: #000;--bslib-color-bg: #698d8f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #698d8f;color:#000}.bg-gradient-teal-red{--bslib-color-fg: #000;--bslib-color-bg: #6b8e76;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #6b8e76;color:#000}.bg-gradient-teal-orange{--bslib-color-fg: #000;--bslib-color-bg: #78ab63;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #78ab63;color:#000}.bg-gradient-teal-yellow{--bslib-color-fg: #000;--bslib-color-bg: #79c65d;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #79c65d;color:#000}.bg-gradient-teal-green{--bslib-color-fg: #000;--bslib-color-bg: #1daf7c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #1daf7c;color:#000}.bg-gradient-teal-cyan{--bslib-color-fg: #000;--bslib-color-bg: #18c9bb;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #18c9bb;color:#000}.bg-gradient-cyan-blue{--bslib-color-fg: #000;--bslib-color-bg: #0da5f5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #0da5f5;color:#000}.bg-gradient-cyan-indigo{--bslib-color-fg: #000;--bslib-color-bg: #3180f1;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #3180f1;color:#000}.bg-gradient-cyan-purple{--bslib-color-fg: #000;--bslib-color-bg: #3494dd;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #3494dd;color:#000}.bg-gradient-cyan-pink{--bslib-color-fg: #000;--bslib-color-bg: #5d8ec5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #5d8ec5;color:#000}.bg-gradient-cyan-red{--bslib-color-fg: #000;--bslib-color-bg: #608eac;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #608eac;color:#000}.bg-gradient-cyan-orange{--bslib-color-fg: #000;--bslib-color-bg: #6dac98;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #6dac98;color:#000}.bg-gradient-cyan-yellow{--bslib-color-fg: #000;--bslib-color-bg: #6ec693;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #6ec693;color:#000}.bg-gradient-cyan-green{--bslib-color-fg: #000;--bslib-color-bg: #12afb2;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #12afb2;color:#000}.bg-gradient-cyan-teal{--bslib-color-fg: #000;--bslib-color-bg: #15cacc;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #15cacc;color:#000}.bg-blue{--bslib-color-bg: #0d6efd;--bslib-color-fg: #ffffff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-blue{--bslib-color-fg: #0d6efd;color:var(--bslib-color-fg)}.bg-indigo{--bslib-color-bg: #6610f2;--bslib-color-fg: #ffffff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-indigo{--bslib-color-fg: #6610f2;color:var(--bslib-color-fg)}.bg-purple{--bslib-color-bg: #6f42c1;--bslib-color-fg: #ffffff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-purple{--bslib-color-fg: #6f42c1;color:var(--bslib-color-fg)}.bg-pink{--bslib-color-bg: #d63384;--bslib-color-fg: #ffffff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-pink{--bslib-color-fg: #d63384;color:var(--bslib-color-fg)}.bg-red{--bslib-color-bg: #dc3545;--bslib-color-fg: #ffffff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-red{--bslib-color-fg: #dc3545;color:var(--bslib-color-fg)}.bg-orange{--bslib-color-bg: #fd7e14;--bslib-color-fg: #000;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-orange{--bslib-color-fg: #fd7e14;color:var(--bslib-color-fg)}.bg-yellow{--bslib-color-bg: #ffc107;--bslib-color-fg: #000;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-yellow{--bslib-color-fg: #ffc107;color:var(--bslib-color-fg)}.bg-green{--bslib-color-bg: #198754;--bslib-color-fg: #ffffff;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-green{--bslib-color-fg: #198754;color:var(--bslib-color-fg)}.bg-teal{--bslib-color-bg: #20c997;--bslib-color-fg: #000;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-teal{--bslib-color-fg: #20c997;color:var(--bslib-color-fg)}.bg-cyan{--bslib-color-bg: #0dcaf0;--bslib-color-fg: #000;background-color:var(--bslib-color-bg);color:var(--bslib-color-fg)}.text-cyan{--bslib-color-fg: #0dcaf0;color:var(--bslib-color-fg)}.text-default{--bslib-color-fg: #dee2e6}.bg-default{--bslib-color-bg: #dee2e6;--bslib-color-fg: #000}.text-primary{--bslib-color-fg: #0d6efd}.bg-primary{--bslib-color-bg: #0d6efd;--bslib-color-fg: #ffffff}.text-secondary{--bslib-color-fg: #6c757d}.bg-secondary{--bslib-color-bg: #6c757d;--bslib-color-fg: #ffffff}.text-success{--bslib-color-fg: #198754}.bg-success{--bslib-color-bg: #198754;--bslib-color-fg: #ffffff}.text-info{--bslib-color-fg: #0dcaf0}.bg-info{--bslib-color-bg: #0dcaf0;--bslib-color-fg: #000}.text-warning{--bslib-color-fg: #ffc107}.bg-warning{--bslib-color-bg: #ffc107;--bslib-color-fg: #000}.text-danger{--bslib-color-fg: #dc3545}.bg-danger{--bslib-color-bg: #dc3545;--bslib-color-fg: #ffffff}.text-light{--bslib-color-fg: #f8f9fa}.bg-light{--bslib-color-bg: #f8f9fa;--bslib-color-fg: #000}.text-dark{--bslib-color-fg: #212529}.bg-dark{--bslib-color-bg: #212529;--bslib-color-fg: #ffffff}.bg-gradient-blue-indigo{--bslib-color-fg: #ffffff;--bslib-color-bg: #3148f9;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #3148f9;color:#fff}.bg-gradient-blue-purple{--bslib-color-fg: #ffffff;--bslib-color-bg: #345ce5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #345ce5;color:#fff}.bg-gradient-blue-pink{--bslib-color-fg: #ffffff;--bslib-color-bg: #5d56cd;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #5d56cd;color:#fff}.bg-gradient-blue-red{--bslib-color-fg: #ffffff;--bslib-color-bg: #6057b3;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #6057b3;color:#fff}.bg-gradient-blue-orange{--bslib-color-fg: #ffffff;--bslib-color-bg: #6d74a0;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #6d74a0;color:#fff}.bg-gradient-blue-yellow{--bslib-color-fg: #000;--bslib-color-bg: #6e8f9b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #6e8f9b;color:#000}.bg-gradient-blue-green{--bslib-color-fg: #ffffff;--bslib-color-bg: #1278b9;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #1278b9;color:#fff}.bg-gradient-blue-teal{--bslib-color-fg: #000;--bslib-color-bg: #1592d4;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #1592d4;color:#000}.bg-gradient-blue-cyan{--bslib-color-fg: #000;--bslib-color-bg: #0d93f8;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0d6efd var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #0d93f8;color:#000}.bg-gradient-indigo-blue{--bslib-color-fg: #ffffff;--bslib-color-bg: #4236f6;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #4236f6;color:#fff}.bg-gradient-indigo-purple{--bslib-color-fg: #ffffff;--bslib-color-bg: #6a24de;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #6a24de;color:#fff}.bg-gradient-indigo-pink{--bslib-color-fg: #ffffff;--bslib-color-bg: #931ec6;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #931ec6;color:#fff}.bg-gradient-indigo-red{--bslib-color-fg: #ffffff;--bslib-color-bg: #951fad;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #951fad;color:#fff}.bg-gradient-indigo-orange{--bslib-color-fg: #ffffff;--bslib-color-bg: #a23c99;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #a23c99;color:#fff}.bg-gradient-indigo-yellow{--bslib-color-fg: #ffffff;--bslib-color-bg: #a35794;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #a35794;color:#fff}.bg-gradient-indigo-green{--bslib-color-fg: #ffffff;--bslib-color-bg: #4740b3;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #4740b3;color:#fff}.bg-gradient-indigo-teal{--bslib-color-fg: #ffffff;--bslib-color-bg: #4a5ace;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #4a5ace;color:#fff}.bg-gradient-indigo-cyan{--bslib-color-fg: #ffffff;--bslib-color-bg: #425af1;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6610f2 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #425af1;color:#fff}.bg-gradient-purple-blue{--bslib-color-fg: #ffffff;--bslib-color-bg: #4854d9;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #4854d9;color:#fff}.bg-gradient-purple-indigo{--bslib-color-fg: #ffffff;--bslib-color-bg: #6b2ed5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #6b2ed5;color:#fff}.bg-gradient-purple-pink{--bslib-color-fg: #ffffff;--bslib-color-bg: #983ca9;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #983ca9;color:#fff}.bg-gradient-purple-red{--bslib-color-fg: #ffffff;--bslib-color-bg: #9b3d8f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #9b3d8f;color:#fff}.bg-gradient-purple-orange{--bslib-color-fg: #ffffff;--bslib-color-bg: #a85a7c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #a85a7c;color:#fff}.bg-gradient-purple-yellow{--bslib-color-fg: #000;--bslib-color-bg: #a97577;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #a97577;color:#000}.bg-gradient-purple-green{--bslib-color-fg: #ffffff;--bslib-color-bg: #4d5e95;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #4d5e95;color:#fff}.bg-gradient-purple-teal{--bslib-color-fg: #ffffff;--bslib-color-bg: #4f78b0;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #4f78b0;color:#fff}.bg-gradient-purple-cyan{--bslib-color-fg: #000;--bslib-color-bg: #4878d4;background:linear-gradient(var(--bg-gradient-deg, 140deg), #6f42c1 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #4878d4;color:#000}.bg-gradient-pink-blue{--bslib-color-fg: #ffffff;--bslib-color-bg: #864bb4;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #864bb4;color:#fff}.bg-gradient-pink-indigo{--bslib-color-fg: #ffffff;--bslib-color-bg: #a925b0;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #a925b0;color:#fff}.bg-gradient-pink-purple{--bslib-color-fg: #ffffff;--bslib-color-bg: #ad399c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #ad399c;color:#fff}.bg-gradient-pink-red{--bslib-color-fg: #ffffff;--bslib-color-bg: #d8346b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #d8346b;color:#fff}.bg-gradient-pink-orange{--bslib-color-fg: #000;--bslib-color-bg: #e65157;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #e65157;color:#000}.bg-gradient-pink-yellow{--bslib-color-fg: #000;--bslib-color-bg: #e66c52;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #e66c52;color:#000}.bg-gradient-pink-green{--bslib-color-fg: #ffffff;--bslib-color-bg: #8a5571;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #8a5571;color:#fff}.bg-gradient-pink-teal{--bslib-color-fg: #000;--bslib-color-bg: #8d6f8c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #8d6f8c;color:#000}.bg-gradient-pink-cyan{--bslib-color-fg: #000;--bslib-color-bg: #866faf;background:linear-gradient(var(--bg-gradient-deg, 140deg), #d63384 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #866faf;color:#000}.bg-gradient-red-blue{--bslib-color-fg: #ffffff;--bslib-color-bg: #894c8f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #894c8f;color:#fff}.bg-gradient-red-indigo{--bslib-color-fg: #ffffff;--bslib-color-bg: #ad268a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #ad268a;color:#fff}.bg-gradient-red-purple{--bslib-color-fg: #ffffff;--bslib-color-bg: #b03a77;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #b03a77;color:#fff}.bg-gradient-red-pink{--bslib-color-fg: #ffffff;--bslib-color-bg: #da345e;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #da345e;color:#fff}.bg-gradient-red-orange{--bslib-color-fg: #000;--bslib-color-bg: #e95231;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #e95231;color:#000}.bg-gradient-red-yellow{--bslib-color-fg: #000;--bslib-color-bg: #ea6d2c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #ea6d2c;color:#000}.bg-gradient-red-green{--bslib-color-fg: #ffffff;--bslib-color-bg: #8e564b;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #8e564b;color:#fff}.bg-gradient-red-teal{--bslib-color-fg: #000;--bslib-color-bg: #917066;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #917066;color:#000}.bg-gradient-red-cyan{--bslib-color-fg: #000;--bslib-color-bg: #897189;background:linear-gradient(var(--bg-gradient-deg, 140deg), #dc3545 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #897189;color:#000}.bg-gradient-orange-blue{--bslib-color-fg: #000;--bslib-color-bg: #9d7871;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #9d7871;color:#000}.bg-gradient-orange-indigo{--bslib-color-fg: #000;--bslib-color-bg: #c1526d;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #c1526d;color:#000}.bg-gradient-orange-purple{--bslib-color-fg: #000;--bslib-color-bg: #c46659;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #c46659;color:#000}.bg-gradient-orange-pink{--bslib-color-fg: #000;--bslib-color-bg: #ed6041;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #ed6041;color:#000}.bg-gradient-orange-red{--bslib-color-fg: #000;--bslib-color-bg: #f06128;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #f06128;color:#000}.bg-gradient-orange-yellow{--bslib-color-fg: #000;--bslib-color-bg: #fe990f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #fe990f;color:#000}.bg-gradient-orange-green{--bslib-color-fg: #000;--bslib-color-bg: #a2822e;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #a2822e;color:#000}.bg-gradient-orange-teal{--bslib-color-fg: #000;--bslib-color-bg: #a59c48;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #a59c48;color:#000}.bg-gradient-orange-cyan{--bslib-color-fg: #000;--bslib-color-bg: #9d9c6c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #fd7e14 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #9d9c6c;color:#000}.bg-gradient-yellow-blue{--bslib-color-fg: #000;--bslib-color-bg: #9ea069;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #9ea069;color:#000}.bg-gradient-yellow-indigo{--bslib-color-fg: #000;--bslib-color-bg: #c27a65;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #c27a65;color:#000}.bg-gradient-yellow-purple{--bslib-color-fg: #000;--bslib-color-bg: #c58e51;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #c58e51;color:#000}.bg-gradient-yellow-pink{--bslib-color-fg: #000;--bslib-color-bg: #ef8839;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #ef8839;color:#000}.bg-gradient-yellow-red{--bslib-color-fg: #000;--bslib-color-bg: #f18920;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #f18920;color:#000}.bg-gradient-yellow-orange{--bslib-color-fg: #000;--bslib-color-bg: #fea60c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #fea60c;color:#000}.bg-gradient-yellow-green{--bslib-color-fg: #000;--bslib-color-bg: #a3aa26;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #a3aa26;color:#000}.bg-gradient-yellow-teal{--bslib-color-fg: #000;--bslib-color-bg: #a6c441;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #a6c441;color:#000}.bg-gradient-yellow-cyan{--bslib-color-fg: #000;--bslib-color-bg: #9ec564;background:linear-gradient(var(--bg-gradient-deg, 140deg), #ffc107 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #9ec564;color:#000}.bg-gradient-green-blue{--bslib-color-fg: #ffffff;--bslib-color-bg: #147d98;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #147d98;color:#fff}.bg-gradient-green-indigo{--bslib-color-fg: #ffffff;--bslib-color-bg: #385793;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #385793;color:#fff}.bg-gradient-green-purple{--bslib-color-fg: #ffffff;--bslib-color-bg: #3b6b80;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #3b6b80;color:#fff}.bg-gradient-green-pink{--bslib-color-fg: #ffffff;--bslib-color-bg: #656567;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #656567;color:#fff}.bg-gradient-green-red{--bslib-color-fg: #ffffff;--bslib-color-bg: #67664e;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #67664e;color:#fff}.bg-gradient-green-orange{--bslib-color-fg: #000;--bslib-color-bg: #74833a;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #74833a;color:#000}.bg-gradient-green-yellow{--bslib-color-fg: #000;--bslib-color-bg: #759e35;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #759e35;color:#000}.bg-gradient-green-teal{--bslib-color-fg: #000;--bslib-color-bg: #1ca16f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #1ca16f;color:#000}.bg-gradient-green-cyan{--bslib-color-fg: #000;--bslib-color-bg: #14a292;background:linear-gradient(var(--bg-gradient-deg, 140deg), #198754 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #14a292;color:#000}.bg-gradient-teal-blue{--bslib-color-fg: #000;--bslib-color-bg: #18a5c0;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #18a5c0;color:#000}.bg-gradient-teal-indigo{--bslib-color-fg: #000;--bslib-color-bg: #3c7fbb;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #3c7fbb;color:#000}.bg-gradient-teal-purple{--bslib-color-fg: #000;--bslib-color-bg: #4093a8;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #4093a8;color:#000}.bg-gradient-teal-pink{--bslib-color-fg: #000;--bslib-color-bg: #698d8f;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #698d8f;color:#000}.bg-gradient-teal-red{--bslib-color-fg: #000;--bslib-color-bg: #6b8e76;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #6b8e76;color:#000}.bg-gradient-teal-orange{--bslib-color-fg: #000;--bslib-color-bg: #78ab63;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #78ab63;color:#000}.bg-gradient-teal-yellow{--bslib-color-fg: #000;--bslib-color-bg: #79c65d;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #79c65d;color:#000}.bg-gradient-teal-green{--bslib-color-fg: #000;--bslib-color-bg: #1daf7c;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #1daf7c;color:#000}.bg-gradient-teal-cyan{--bslib-color-fg: #000;--bslib-color-bg: #18c9bb;background:linear-gradient(var(--bg-gradient-deg, 140deg), #20c997 var(--bg-gradient-start, 36%), #0dcaf0 var(--bg-gradient-end, 180%)) #18c9bb;color:#000}.bg-gradient-cyan-blue{--bslib-color-fg: #000;--bslib-color-bg: #0da5f5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #0d6efd var(--bg-gradient-end, 180%)) #0da5f5;color:#000}.bg-gradient-cyan-indigo{--bslib-color-fg: #000;--bslib-color-bg: #3180f1;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #6610f2 var(--bg-gradient-end, 180%)) #3180f1;color:#000}.bg-gradient-cyan-purple{--bslib-color-fg: #000;--bslib-color-bg: #3494dd;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #6f42c1 var(--bg-gradient-end, 180%)) #3494dd;color:#000}.bg-gradient-cyan-pink{--bslib-color-fg: #000;--bslib-color-bg: #5d8ec5;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #d63384 var(--bg-gradient-end, 180%)) #5d8ec5;color:#000}.bg-gradient-cyan-red{--bslib-color-fg: #000;--bslib-color-bg: #608eac;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #dc3545 var(--bg-gradient-end, 180%)) #608eac;color:#000}.bg-gradient-cyan-orange{--bslib-color-fg: #000;--bslib-color-bg: #6dac98;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #fd7e14 var(--bg-gradient-end, 180%)) #6dac98;color:#000}.bg-gradient-cyan-yellow{--bslib-color-fg: #000;--bslib-color-bg: #6ec693;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #ffc107 var(--bg-gradient-end, 180%)) #6ec693;color:#000}.bg-gradient-cyan-green{--bslib-color-fg: #000;--bslib-color-bg: #12afb2;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #198754 var(--bg-gradient-end, 180%)) #12afb2;color:#000}.bg-gradient-cyan-teal{--bslib-color-fg: #000;--bslib-color-bg: #15cacc;background:linear-gradient(var(--bg-gradient-deg, 140deg), #0dcaf0 var(--bg-gradient-start, 36%), #20c997 var(--bg-gradient-end, 180%)) #15cacc;color:#000}:root{--bslib-spacer: 1rem;--bslib-mb-spacer: var(--bslib-spacer, 1rem)}.bslib-mb-spacing{margin-bottom:var(--bslib-mb-spacer)}.bslib-gap-spacing{gap:var(--bslib-mb-spacer)}.bslib-gap-spacing>.bslib-mb-spacing,.bslib-gap-spacing>.form-group,.bslib-gap-spacing>p,.bslib-gap-spacing>pre{margin-bottom:0}.html-fill-container>.html-fill-item.bslib-mb-spacing{margin-bottom:0}.tab-content>.tab-pane.html-fill-container{display:none}.tab-content>.active.html-fill-container{display:flex}.tab-content.html-fill-container{padding:0}.accordion .accordion-header{font-size:calc(1.29rem + 0.48vw);margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2;color:var(--bs-heading-color);margin-bottom:0}@media(min-width: 1200px){.accordion .accordion-header{font-size:1.65rem}}.accordion .accordion-icon:not(:empty){margin-right:.75rem;display:flex}.accordion .accordion-button:not(.collapsed){box-shadow:none}.accordion .accordion-button:not(.collapsed):focus{box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.bslib-card{overflow:auto}.bslib-card .card-body+.card-body{padding-top:0}.bslib-card .card-body{overflow:auto}.bslib-card .card-body p{margin-top:0}.bslib-card .card-body p:last-child{margin-bottom:0}.bslib-card .card-body{max-height:var(--bslib-card-body-max-height, none)}.bslib-card[data-full-screen=true]>.card-body{max-height:var(--bslib-card-body-max-height-full-screen, none)}.bslib-card .card-header .form-group{margin-bottom:0}.bslib-card .card-header .selectize-control{margin-bottom:0}.bslib-card .card-header .selectize-control .item{margin-right:1.15rem}.bslib-card .card-footer{margin-top:auto}.bslib-card .bslib-navs-card-title{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center}.bslib-card .bslib-navs-card-title .nav{margin-left:auto}.bslib-card .bslib-sidebar-layout:not([data-bslib-sidebar-border=true]){border:none}.bslib-card .bslib-sidebar-layout:not([data-bslib-sidebar-border-radius=true]){border-top-left-radius:0;border-top-right-radius:0}[data-full-screen=true]{position:fixed;inset:3.5rem 1rem 1rem;height:auto !important;max-height:none !important;width:auto !important;z-index:1070}.bslib-full-screen-enter{display:none;position:absolute;bottom:var(--bslib-full-screen-enter-bottom, 0.2rem);right:var(--bslib-full-screen-enter-right, 0);top:var(--bslib-full-screen-enter-top);left:var(--bslib-full-screen-enter-left);color:var(--bslib-color-fg, var(--bs-card-color));background-color:var(--bslib-color-bg, var(--bs-card-bg, var(--bs-body-bg)));border:var(--bs-card-border-width) solid var(--bslib-color-fg, var(--bs-card-border-color));box-shadow:0 2px 4px rgba(0,0,0,.15);margin:.2rem .4rem;padding:.55rem !important;font-size:.8rem;cursor:pointer;opacity:.7;z-index:1070}.bslib-full-screen-enter:hover{opacity:1}.card[data-full-screen=false]:hover>*>.bslib-full-screen-enter{display:block}.bslib-has-full-screen .card:hover>*>.bslib-full-screen-enter{display:none}@media(max-width: 575.98px){.bslib-full-screen-enter{display:none !important}}.bslib-full-screen-exit{position:relative;top:1.35rem;font-size:.9rem;cursor:pointer;text-decoration:none;display:flex;float:right;margin-right:2.15rem;align-items:center;color:rgba(var(--bs-body-bg-rgb), 0.8)}.bslib-full-screen-exit:hover{color:rgba(var(--bs-body-bg-rgb), 1)}.bslib-full-screen-exit svg{margin-left:.5rem;font-size:1.5rem}#bslib-full-screen-overlay{position:fixed;inset:0;background-color:rgba(var(--bs-body-color-rgb), 0.6);backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);z-index:1069;animation:bslib-full-screen-overlay-enter 400ms cubic-bezier(0.6, 0.02, 0.65, 1) forwards}@keyframes bslib-full-screen-overlay-enter{0%{opacity:0}100%{opacity:1}}.bslib-grid{display:grid !important;gap:var(--bslib-spacer, 1rem);height:var(--bslib-grid-height)}.bslib-grid.grid{grid-template-columns:repeat(var(--bs-columns, 12), minmax(0, 1fr));grid-template-rows:unset;grid-auto-rows:var(--bslib-grid--row-heights);--bslib-grid--row-heights--xs: unset;--bslib-grid--row-heights--sm: unset;--bslib-grid--row-heights--md: unset;--bslib-grid--row-heights--lg: unset;--bslib-grid--row-heights--xl: unset;--bslib-grid--row-heights--xxl: unset}.bslib-grid.grid.bslib-grid--row-heights--xs{--bslib-grid--row-heights: var(--bslib-grid--row-heights--xs)}@media(min-width: 576px){.bslib-grid.grid.bslib-grid--row-heights--sm{--bslib-grid--row-heights: var(--bslib-grid--row-heights--sm)}}@media(min-width: 768px){.bslib-grid.grid.bslib-grid--row-heights--md{--bslib-grid--row-heights: var(--bslib-grid--row-heights--md)}}@media(min-width: 992px){.bslib-grid.grid.bslib-grid--row-heights--lg{--bslib-grid--row-heights: var(--bslib-grid--row-heights--lg)}}@media(min-width: 1200px){.bslib-grid.grid.bslib-grid--row-heights--xl{--bslib-grid--row-heights: var(--bslib-grid--row-heights--xl)}}@media(min-width: 1400px){.bslib-grid.grid.bslib-grid--row-heights--xxl{--bslib-grid--row-heights: var(--bslib-grid--row-heights--xxl)}}.bslib-grid>*>.shiny-input-container{width:100%}.bslib-grid-item{grid-column:auto/span 1}@media(max-width: 767.98px){.bslib-grid-item{grid-column:1/-1}}@media(max-width: 575.98px){.bslib-grid{grid-template-columns:1fr !important;height:var(--bslib-grid-height-mobile)}.bslib-grid.grid{height:unset !important;grid-auto-rows:var(--bslib-grid--row-heights--xs, auto)}}@media(min-width: 576px){.nav:not(.nav-hidden){display:flex !important;display:-webkit-flex !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column){float:none !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column)>.bslib-nav-spacer{margin-left:auto !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column)>.form-inline{margin-top:auto;margin-bottom:auto}.nav:not(.nav-hidden).nav-stacked{flex-direction:column;-webkit-flex-direction:column;height:100%}.nav:not(.nav-hidden).nav-stacked>.bslib-nav-spacer{margin-top:auto !important}}html{height:100%}.bslib-page-fill{width:100%;height:100%;margin:0;padding:var(--bslib-spacer, 1rem);gap:var(--bslib-spacer, 1rem)}@media(max-width: 575.98px){.bslib-page-fill{height:var(--bslib-page-fill-mobile-height, auto)}}.navbar+.container-fluid:has(>.tab-content>.tab-pane.active.html-fill-container),.navbar+.container-sm:has(>.tab-content>.tab-pane.active.html-fill-container),.navbar+.container-md:has(>.tab-content>.tab-pane.active.html-fill-container),.navbar+.container-lg:has(>.tab-content>.tab-pane.active.html-fill-container),.navbar+.container-xl:has(>.tab-content>.tab-pane.active.html-fill-container),.navbar+.container-xxl:has(>.tab-content>.tab-pane.active.html-fill-container){padding-left:0;padding-right:0}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container,.navbar+.container-sm>.tab-content>.tab-pane.active.html-fill-container,.navbar+.container-md>.tab-content>.tab-pane.active.html-fill-container,.navbar+.container-lg>.tab-content>.tab-pane.active.html-fill-container,.navbar+.container-xl>.tab-content>.tab-pane.active.html-fill-container,.navbar+.container-xxl>.tab-content>.tab-pane.active.html-fill-container{padding:var(--bslib-spacer, 1rem);gap:var(--bslib-spacer, 1rem)}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container:has(>.bslib-sidebar-layout:only-child),.navbar+.container-sm>.tab-content>.tab-pane.active.html-fill-container:has(>.bslib-sidebar-layout:only-child),.navbar+.container-md>.tab-content>.tab-pane.active.html-fill-container:has(>.bslib-sidebar-layout:only-child),.navbar+.container-lg>.tab-content>.tab-pane.active.html-fill-container:has(>.bslib-sidebar-layout:only-child),.navbar+.container-xl>.tab-content>.tab-pane.active.html-fill-container:has(>.bslib-sidebar-layout:only-child),.navbar+.container-xxl>.tab-content>.tab-pane.active.html-fill-container:has(>.bslib-sidebar-layout:only-child){padding:0}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border=true]),.navbar+.container-sm>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border=true]),.navbar+.container-md>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border=true]),.navbar+.container-lg>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border=true]),.navbar+.container-xl>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border=true]),.navbar+.container-xxl>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border=true]){border-left:none;border-right:none;border-bottom:none}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border-radius=true]),.navbar+.container-sm>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border-radius=true]),.navbar+.container-md>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border-radius=true]),.navbar+.container-lg>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border-radius=true]),.navbar+.container-xl>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border-radius=true]),.navbar+.container-xxl>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border-radius=true]){border-radius:0}.navbar+div>.bslib-sidebar-layout{border-top:var(--bslib-sidebar-border)}:root{--bslib-page-sidebar-title-bg: #517699;--bslib-page-sidebar-title-color: #ffffff}.bslib-page-title{background-color:var(--bslib-page-sidebar-title-bg);color:var(--bslib-page-sidebar-title-color);font-size:1.25rem;font-weight:300;padding:var(--bslib-spacer, 1rem);padding-left:1.5rem;margin-bottom:0;border-bottom:1px solid #dee2e6}.bslib-sidebar-layout{--bslib-sidebar-transition-duration: 500ms;--bslib-sidebar-transition-easing-x: cubic-bezier(0.8, 0.78, 0.22, 1.07);--bslib-sidebar-border: var(--bs-card-border-width, 1px) solid var(--bs-card-border-color, rgba(0, 0, 0, 0.175));--bslib-sidebar-border-radius: var(--bs-border-radius);--bslib-sidebar-vert-border: var(--bs-card-border-width, 1px) solid var(--bs-card-border-color, rgba(0, 0, 0, 0.175));--bslib-sidebar-bg: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.05);--bslib-sidebar-fg: var(--bs-emphasis-color, black);--bslib-sidebar-main-fg: var(--bs-card-color, var(--bs-body-color));--bslib-sidebar-main-bg: var(--bs-card-bg, var(--bs-body-bg));--bslib-sidebar-toggle-bg: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.1);--bslib-sidebar-padding: calc(var(--bslib-spacer) * 1.5);--bslib-sidebar-icon-size: var(--bslib-spacer, 1rem);--bslib-sidebar-icon-button-size: calc(var(--bslib-sidebar-icon-size, 1rem) * 2);--bslib-sidebar-padding-icon: calc(var(--bslib-sidebar-icon-button-size, 2rem) * 1.5);--bslib-collapse-toggle-border-radius: var(--bs-border-radius, 0.25rem);--bslib-collapse-toggle-transform: 0deg;--bslib-sidebar-toggle-transition-easing: cubic-bezier(1, 0, 0, 1);--bslib-collapse-toggle-right-transform: 180deg;--bslib-sidebar-column-main: minmax(0, 1fr);display:grid !important;grid-template-columns:min(100% - var(--bslib-sidebar-icon-size),var(--bslib-sidebar-width, 250px)) var(--bslib-sidebar-column-main);position:relative;transition:grid-template-columns ease-in-out var(--bslib-sidebar-transition-duration);border:var(--bslib-sidebar-border);border-radius:var(--bslib-sidebar-border-radius)}@media(prefers-reduced-motion: reduce){.bslib-sidebar-layout{transition:none}}.bslib-sidebar-layout[data-bslib-sidebar-border=false]{border:none}.bslib-sidebar-layout[data-bslib-sidebar-border-radius=false]{border-radius:initial}.bslib-sidebar-layout>.main,.bslib-sidebar-layout>.sidebar{grid-row:1/2;border-radius:inherit;overflow:auto}.bslib-sidebar-layout>.main{grid-column:2/3;border-top-left-radius:0;border-bottom-left-radius:0;padding:var(--bslib-sidebar-padding);transition:padding var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration);color:var(--bslib-sidebar-main-fg);background-color:var(--bslib-sidebar-main-bg)}.bslib-sidebar-layout>.sidebar{grid-column:1/2;width:100%;height:100%;border-right:var(--bslib-sidebar-vert-border);border-top-right-radius:0;border-bottom-right-radius:0;color:var(--bslib-sidebar-fg);background-color:var(--bslib-sidebar-bg);backdrop-filter:blur(5px)}.bslib-sidebar-layout>.sidebar>.sidebar-content{display:flex;flex-direction:column;gap:var(--bslib-spacer, 1rem);padding:var(--bslib-sidebar-padding);padding-top:var(--bslib-sidebar-padding-icon)}.bslib-sidebar-layout>.sidebar>.sidebar-content>:last-child:not(.sidebar-title){margin-bottom:0}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion{margin-left:calc(-1*var(--bslib-sidebar-padding));margin-right:calc(-1*var(--bslib-sidebar-padding))}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:last-child{margin-bottom:calc(-1*var(--bslib-sidebar-padding))}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:not(:last-child){margin-bottom:1rem}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion .accordion-body{display:flex;flex-direction:column}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:not(:first-child) .accordion-item:first-child{border-top:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:not(:last-child) .accordion-item:last-child{border-bottom:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.bslib-sidebar-layout>.sidebar>.sidebar-content.has-accordion>.sidebar-title{border-bottom:none;padding-bottom:0}.bslib-sidebar-layout>.sidebar .shiny-input-container{width:100%}.bslib-sidebar-layout[data-bslib-sidebar-open=always]>.sidebar>.sidebar-content{padding-top:var(--bslib-sidebar-padding)}.bslib-sidebar-layout>.collapse-toggle{grid-row:1/2;grid-column:1/2;display:inline-flex;align-items:center;position:absolute;right:calc(var(--bslib-sidebar-icon-size));top:calc(var(--bslib-sidebar-icon-size, 1rem)/2);border:none;border-radius:var(--bslib-collapse-toggle-border-radius);height:var(--bslib-sidebar-icon-button-size, 2rem);width:var(--bslib-sidebar-icon-button-size, 2rem);display:flex;align-items:center;justify-content:center;padding:0;color:var(--bslib-sidebar-fg);background-color:unset;transition:color var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration),top var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration),right var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration),left var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration)}.bslib-sidebar-layout>.collapse-toggle:hover{background-color:var(--bslib-sidebar-toggle-bg)}.bslib-sidebar-layout>.collapse-toggle>.collapse-icon{opacity:.8;width:var(--bslib-sidebar-icon-size);height:var(--bslib-sidebar-icon-size);transform:rotateY(var(--bslib-collapse-toggle-transform));transition:transform var(--bslib-sidebar-toggle-transition-easing) var(--bslib-sidebar-transition-duration)}.bslib-sidebar-layout>.collapse-toggle:hover>.collapse-icon{opacity:1}.bslib-sidebar-layout .sidebar-title{font-size:1.25rem;line-height:1.25;margin-top:0;margin-bottom:1rem;padding-bottom:1rem;border-bottom:var(--bslib-sidebar-border)}.bslib-sidebar-layout.sidebar-right{grid-template-columns:var(--bslib-sidebar-column-main) min(100% - var(--bslib-sidebar-icon-size),var(--bslib-sidebar-width, 250px))}.bslib-sidebar-layout.sidebar-right>.main{grid-column:1/2;border-top-right-radius:0;border-bottom-right-radius:0;border-top-left-radius:inherit;border-bottom-left-radius:inherit}.bslib-sidebar-layout.sidebar-right>.sidebar{grid-column:2/3;border-right:none;border-left:var(--bslib-sidebar-vert-border);border-top-left-radius:0;border-bottom-left-radius:0}.bslib-sidebar-layout.sidebar-right>.collapse-toggle{grid-column:2/3;left:var(--bslib-sidebar-icon-size);right:unset;border:var(--bslib-collapse-toggle-border)}.bslib-sidebar-layout.sidebar-right>.collapse-toggle>.collapse-icon{transform:rotateY(var(--bslib-collapse-toggle-right-transform))}.bslib-sidebar-layout.sidebar-collapsed{--bslib-collapse-toggle-transform: 180deg;--bslib-collapse-toggle-right-transform: 0deg;--bslib-sidebar-vert-border: none;grid-template-columns:0 minmax(0, 1fr)}.bslib-sidebar-layout.sidebar-collapsed.sidebar-right{grid-template-columns:minmax(0, 1fr) 0}.bslib-sidebar-layout.sidebar-collapsed:not(.transitioning)>.sidebar>*{display:none}.bslib-sidebar-layout.sidebar-collapsed>.main{border-radius:inherit}.bslib-sidebar-layout.sidebar-collapsed:not(.sidebar-right)>.main{padding-left:var(--bslib-sidebar-padding-icon)}.bslib-sidebar-layout.sidebar-collapsed.sidebar-right>.main{padding-right:var(--bslib-sidebar-padding-icon)}.bslib-sidebar-layout.sidebar-collapsed>.collapse-toggle{color:var(--bslib-sidebar-main-fg);top:calc(var(--bslib-sidebar-overlap-counter, 0)*(var(--bslib-sidebar-icon-size) + var(--bslib-sidebar-padding)) + var(--bslib-sidebar-icon-size, 1rem)/2);right:calc(-2.5*var(--bslib-sidebar-icon-size) - var(--bs-card-border-width, 1px))}.bslib-sidebar-layout.sidebar-collapsed.sidebar-right>.collapse-toggle{left:calc(-2.5*var(--bslib-sidebar-icon-size) - var(--bs-card-border-width, 1px));right:unset}@media(min-width: 576px){.bslib-sidebar-layout.transitioning>.sidebar>.sidebar-content{display:none}}@media(max-width: 575.98px){.bslib-sidebar-layout[data-bslib-sidebar-open=desktop]{--bslib-sidebar-js-init-collapsed: true}.bslib-sidebar-layout>.sidebar,.bslib-sidebar-layout.sidebar-right>.sidebar{border:none}.bslib-sidebar-layout>.main,.bslib-sidebar-layout.sidebar-right>.main{grid-column:1/3}.bslib-sidebar-layout[data-bslib-sidebar-open=always]{display:block !important}.bslib-sidebar-layout[data-bslib-sidebar-open=always]>.sidebar{max-height:var(--bslib-sidebar-max-height-mobile);overflow-y:auto;border-top:var(--bslib-sidebar-vert-border)}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]){grid-template-columns:100% 0}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]):not(.sidebar-collapsed)>.sidebar{z-index:1}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]):not(.sidebar-collapsed)>.collapse-toggle{z-index:1}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]).sidebar-right{grid-template-columns:0 100%}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]).sidebar-collapsed{grid-template-columns:0 100%}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]).sidebar-collapsed.sidebar-right{grid-template-columns:100% 0}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]):not(.sidebar-right)>.main{padding-left:var(--bslib-sidebar-padding-icon)}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]).sidebar-right>.main{padding-right:var(--bslib-sidebar-padding-icon)}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always])>.main{opacity:0;transition:opacity var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration)}.bslib-sidebar-layout:not([data-bslib-sidebar-open=always]).sidebar-collapsed>.main{opacity:1}}:root{--bslib-value-box-shadow: none;--bslib-value-box-border-width-auto-yes: var(--bslib-value-box-border-width-baseline);--bslib-value-box-border-width-auto-no: 0;--bslib-value-box-border-width-baseline: 1px}.bslib-value-box{border-width:var(--bslib-value-box-border-width-auto-no, var(--bslib-value-box-border-width-baseline));container-name:bslib-value-box;container-type:inline-size}.bslib-value-box.card{box-shadow:var(--bslib-value-box-shadow)}.bslib-value-box.border-auto{border-width:var(--bslib-value-box-border-width-auto-yes, var(--bslib-value-box-border-width-baseline))}.bslib-value-box.default{--bslib-value-box-bg-default: var(--bs-card-bg, #ffffff);--bslib-value-box-border-color-default: var(--bs-card-border-color, rgba(0, 0, 0, 0.175));color:var(--bslib-value-box-color);background-color:var(--bslib-value-box-bg, var(--bslib-value-box-bg-default));border-color:var(--bslib-value-box-border-color, var(--bslib-value-box-border-color-default))}.bslib-value-box .value-box-grid{display:grid;grid-template-areas:"left right";align-items:center;overflow:hidden}.bslib-value-box .value-box-showcase{height:100%;max-height:var(---bslib-value-box-showcase-max-h, 100%)}.bslib-value-box .value-box-showcase,.bslib-value-box .value-box-showcase>.html-fill-item{width:100%}.bslib-value-box[data-full-screen=true] .value-box-showcase{max-height:var(---bslib-value-box-showcase-max-h-fs, 100%)}@media screen and (min-width: 575.98px){@container bslib-value-box (max-width: 300px){.bslib-value-box:not(.showcase-bottom) .value-box-grid{grid-template-columns:1fr !important;grid-template-rows:auto auto;grid-template-areas:"top" "bottom"}.bslib-value-box:not(.showcase-bottom) .value-box-grid .value-box-showcase{grid-area:top !important}.bslib-value-box:not(.showcase-bottom) .value-box-grid .value-box-area{grid-area:bottom !important;justify-content:end}}}.bslib-value-box .value-box-area{justify-content:center;padding:1.5rem 1rem;font-size:.9rem;font-weight:500}.bslib-value-box .value-box-area *{margin-bottom:0;margin-top:0}.bslib-value-box .value-box-title{font-size:1rem;margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.bslib-value-box .value-box-title:empty::after{content:" "}.bslib-value-box .value-box-value{font-size:calc(1.29rem + 0.48vw);margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}@media(min-width: 1200px){.bslib-value-box .value-box-value{font-size:1.65rem}}.bslib-value-box .value-box-value:empty::after{content:" "}.bslib-value-box .value-box-showcase{align-items:center;justify-content:center;margin-top:auto;margin-bottom:auto;padding:1rem}.bslib-value-box .value-box-showcase .bi,.bslib-value-box .value-box-showcase .fa,.bslib-value-box .value-box-showcase .fab,.bslib-value-box .value-box-showcase .fas,.bslib-value-box .value-box-showcase .far{opacity:.85;min-width:50px;max-width:125%}.bslib-value-box .value-box-showcase .bi,.bslib-value-box .value-box-showcase .fa,.bslib-value-box .value-box-showcase .fab,.bslib-value-box .value-box-showcase .fas,.bslib-value-box .value-box-showcase .far{font-size:4rem}.bslib-value-box.showcase-top-right .value-box-grid{grid-template-columns:1fr var(---bslib-value-box-showcase-w, 50%)}.bslib-value-box.showcase-top-right .value-box-grid .value-box-showcase{grid-area:right;margin-left:auto;align-self:start;align-items:end;padding-left:0;padding-bottom:0}.bslib-value-box.showcase-top-right .value-box-grid .value-box-area{grid-area:left;align-self:end}.bslib-value-box.showcase-top-right[data-full-screen=true] .value-box-grid{grid-template-columns:auto var(---bslib-value-box-showcase-w-fs, 1fr)}.bslib-value-box.showcase-top-right[data-full-screen=true] .value-box-grid>div{align-self:center}.bslib-value-box.showcase-top-right:not([data-full-screen=true]) .value-box-showcase{margin-top:0}@container bslib-value-box (max-width: 300px){.bslib-value-box.showcase-top-right:not([data-full-screen=true]) .value-box-grid .value-box-showcase{padding-left:1rem}}.bslib-value-box.showcase-left-center .value-box-grid{grid-template-columns:var(---bslib-value-box-showcase-w, 30%) auto}.bslib-value-box.showcase-left-center[data-full-screen=true] .value-box-grid{grid-template-columns:var(---bslib-value-box-showcase-w-fs, 1fr) auto}.bslib-value-box.showcase-left-center:not([data-fill-screen=true]) .value-box-grid .value-box-showcase{grid-area:left}.bslib-value-box.showcase-left-center:not([data-fill-screen=true]) .value-box-grid .value-box-area{grid-area:right}.bslib-value-box.showcase-bottom .value-box-grid{grid-template-columns:1fr;grid-template-rows:1fr var(---bslib-value-box-showcase-h, auto);grid-template-areas:"top" "bottom";overflow:hidden}.bslib-value-box.showcase-bottom .value-box-grid .value-box-showcase{grid-area:bottom;padding:0;margin:0}.bslib-value-box.showcase-bottom .value-box-grid .value-box-area{grid-area:top}.bslib-value-box.showcase-bottom[data-full-screen=true] .value-box-grid{grid-template-rows:1fr var(---bslib-value-box-showcase-h-fs, 2fr)}.bslib-value-box.showcase-bottom[data-full-screen=true] .value-box-grid .value-box-showcase{padding:1rem}[data-bs-theme=dark] .bslib-value-box{--bslib-value-box-shadow: 0 0.5rem 1rem rgb(0 0 0 / 50%)}.html-fill-container{display:flex;flex-direction:column;min-height:0;min-width:0}.html-fill-container>.html-fill-item{flex:1 1 auto;min-height:0;min-width:0}.html-fill-container>:not(.html-fill-item){flex:0 0 auto}.quarto-container{min-height:calc(100vh - 132px)}body.hypothesis-enabled #quarto-header{margin-right:16px}footer.footer .nav-footer,#quarto-header>nav{padding-left:1em;padding-right:1em}footer.footer div.nav-footer p:first-child{margin-top:0}footer.footer div.nav-footer p:last-child{margin-bottom:0}#quarto-content>*{padding-top:14px}#quarto-content>#quarto-sidebar-glass{padding-top:0px}@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:position 200ms linear;transition:all 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% - 115px);min-width:0;display:flex;align-items:center}@media(min-width: 992px){.navbar-brand-container{margin-right:1em}}.navbar-brand.navbar-brand-logo{margin-right:4px;display:inline-flex}.navbar-toggler{flex-basis:content;flex-shrink:0}.navbar .navbar-brand-container{order:2}.navbar .navbar-toggler{order:1}.navbar .navbar-container>.navbar-nav{order:20}.navbar .navbar-container>.navbar-brand-container{margin-left:0 !important;margin-right:0 !important}.navbar .navbar-collapse{order:20}.navbar #quarto-search{order:4;margin-left:auto}.navbar .navbar-toggler{margin-right:.5em}.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 .quarto-navbar-tools{order:3}.navbar .quarto-navbar-tools div.dropdown{display:inline-block}.navbar .quarto-navbar-tools .quarto-navigation-tool{color:#fdfefe}.navbar .quarto-navbar-tools .quarto-navigation-tool:hover{color:#fdfeff}.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}@media(max-width: 991.98px){#quarto-sidebar div.sidebar-header{padding-top:.2em}}.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 .quarto-navigation-tool{opacity:.7;font-size:.875rem}#quarto-sidebar>nav>.sidebar-tools-main{margin-left:14px}.sidebar-tools-main{display:inline-flex;margin-left:0px;order:2}.sidebar-tools-main:not(.tools-wide){vertical-align:middle}.sidebar-navigation .quarto-navigation-tool.dropdown-toggle::after{display:none}.sidebar.sidebar-navigation>*{padding-top:1em}.sidebar-item{margin-bottom:.2em;line-height:1rem;margin-top:.4rem}.sidebar-section{padding-left:.5em;padding-bottom:.2em}.sidebar-item .sidebar-item-container{display:flex;justify-content:space-between;cursor:pointer}.sidebar-item-toggle:hover{cursor:pointer}.sidebar-item .sidebar-item-toggle .bi{font-size:.7rem;text-align:center}.sidebar-item .sidebar-item-toggle .bi-chevron-right::before{transition:transform 200ms ease}.sidebar-item .sidebar-item-toggle[aria-expanded=false] .bi-chevron-right::before{transform:none}.sidebar-item .sidebar-item-toggle[aria-expanded=true] .bi-chevron-right::before{transform:rotate(90deg)}.sidebar-item-text{width:100%}.sidebar-navigation .sidebar-divider{margin-left:0;margin-right:0;margin-top:.5rem;margin-bottom:.5rem}@media(max-width: 991.98px){.quarto-secondary-nav{display:block}.quarto-secondary-nav button.quarto-search-button{padding-right:0em;padding-left:2em}.quarto-secondary-nav button.quarto-btn-toggle{margin-left:-0.75rem;margin-right:.15rem}.quarto-secondary-nav nav.quarto-title-breadcrumbs{display:none}.quarto-secondary-nav nav.quarto-page-breadcrumbs{display:flex;align-items:center;padding-right:1em;margin-left:-0.25em}.quarto-secondary-nav nav.quarto-page-breadcrumbs a{text-decoration:none}.quarto-secondary-nav nav.quarto-page-breadcrumbs ol.breadcrumb{margin-bottom:0}}@media(min-width: 992px){.quarto-secondary-nav{display:none}}.quarto-title-breadcrumbs .breadcrumb{margin-bottom:.5em;font-size:.9rem}.quarto-title-breadcrumbs .breadcrumb li:last-of-type a{color:#6c757d}.quarto-secondary-nav .quarto-btn-toggle{color:#595959}.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}.no-decor{text-decoration:none}.quarto-secondary-nav-title{margin-top:.3em;color:#595959;padding-top:4px}.quarto-secondary-nav nav.quarto-page-breadcrumbs{color:#595959}.quarto-secondary-nav nav.quarto-page-breadcrumbs a{color:#595959}.quarto-secondary-nav nav.quarto-page-breadcrumbs a:hover{color:rgba(10,83,190,.8)}.quarto-secondary-nav nav.quarto-page-breadcrumbs .breadcrumb-item::before{color:#8c8c8c}.breadcrumb-item{line-height:1.2rem}div.sidebar-item-container{color:#595959}div.sidebar-item-container:hover,div.sidebar-item-container:focus{color:rgba(10,83,190,.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:#0a53be}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{min-height:100%}nav.quarto-secondary-nav{background-color:#fff;border-bottom:1px solid #dee2e6}.quarto-banner nav.quarto-secondary-nav{background-color:#517699;color:#fdfefe;border-top:1px solid #dee2e6}.sidebar .sidebar-footer{visibility:visible;padding-top:1rem;position:inherit}.sidebar-tools-collapse{display:block}}#quarto-sidebar{transition:width .15s ease-in}#quarto-sidebar>*{padding-right:1em}@media(max-width: 991.98px){#quarto-sidebar .sidebar-menu-container{white-space:nowrap;min-width:225px}#quarto-sidebar.show{transition:width .15s ease-out}}@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}}@media(max-width: 991.98px){#quarto-sidebar-glass{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(255,255,255,0);transition:background-color .15s ease-in;z-index:-1}#quarto-sidebar-glass.collapsing{z-index:1000}#quarto-sidebar-glass.show{transition:background-color .15s ease-out;background-color:rgba(102,102,102,.4);z-index:1000}}.sidebar .sidebar-footer{padding:.5rem 1rem;align-self:flex-end;color:#6c757d;width:100%}.quarto-page-breadcrumbs .breadcrumb-item+.breadcrumb-item,.quarto-page-breadcrumbs .breadcrumb-item{padding-right:.33em;padding-left:0}.quarto-page-breadcrumbs .breadcrumb-item::before{padding-right:.33em}.quarto-sidebar-footer{font-size:.875em}.sidebar-section .bi-chevron-right{vertical-align:middle}.sidebar-section .bi-chevron-right::before{font-size:.9em}.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:#0a58ca}.nav-footer .toc-actions{padding-bottom:.5em;padding-top:.5em}.nav-footer .toc-actions a,.nav-footer .toc-actions a:hover{text-decoration:none}.nav-footer .toc-actions ul{display:flex;list-style:none}.nav-footer .toc-actions ul :first-child{margin-left:auto}.nav-footer .toc-actions ul :last-child{margin-right:auto}.nav-footer .toc-actions ul li{padding-right:1.5em}.nav-footer .toc-actions ul li i.bi{padding-right:.4em}.nav-footer .toc-actions ul li:last-of-type{padding-right:0}.nav-footer{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:baseline;text-align:center;padding-top:.5rem;padding-bottom:.5rem;background-color:#fff}body.nav-fixed{padding-top:64px}.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:inline-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{flex:1 1 0px;text-align:left}.nav-footer-right{flex:1 1 0px;text-align:right}.nav-footer-center{flex:1 1 0px;min-height:3em;text-align:center}.nav-footer-center .footer-items{justify-content:center}@media(max-width: 767.98px){.nav-footer-center{margin-top:3em}}.navbar .quarto-reader-toggle.reader .quarto-reader-toggle-btn{background-color:#fdfefe;border-radius:3px}@media(max-width: 991.98px){.quarto-reader-toggle{display:none}}.quarto-reader-toggle.reader.quarto-navigation-tool .quarto-reader-toggle-btn{background-color:#595959;border-radius:3px}.quarto-reader-toggle .quarto-reader-toggle-btn{display:inline-flex;padding-left:.2em;padding-right:.2em;margin-left:-0.2em;margin-right:-0.2em;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,')}#quarto-back-to-top{display:none;position:fixed;bottom:50px;background-color:#fff;border-radius:.25rem;box-shadow:0 .2rem .5rem #6c757d,0 0 .05rem #6c757d;color:#6c757d;text-decoration:none;font-size:.9em;text-align:center;left:50%;padding:.4rem .8rem;transform:translate(-50%, 0)}.aa-DetachedSearchButtonQuery{display:none}.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:.25rem;order:999}}.navbar.navbar-expand-sm #quarto-search,.navbar.navbar-expand-md #quarto-search{order:999}@media(min-width: 992px){.navbar .quarto-navbar-tools{margin-left:auto;order:900}}@media(max-width: 991.98px){#quarto-sidebar .sidebar-search{display:none}}#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:#fdfefe;opacity:1}.navbar #quarto-search.type-overlay .aa-Autocomplete svg.aa-SubmitIcon{width:26px;height:26px;color:#fdfefe;opacity:1}.aa-Autocomplete .aa-Form,.aa-DetachedFormContainer .aa-Form{align-items:center;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;color:#212529;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(13,110,253,.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:#212529;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:#212529;font:inherit;height:calc(1.5em + .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:#212529;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:#212529;opacity:.8;cursor:pointer;display:flex;margin:0;width:calc(1.5em + .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:#212529;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 + calc(1px * 2))}.aa-Autocomplete .aa-Form .aa-InputWrapperSuffix .aa-CopyButton,.aa-DetachedFormContainer .aa-Form .aa-InputWrapperSuffix .aa-CopyButton{border:none;align-items:center;background:none;color:#212529;opacity:.4;font-size:.7rem;cursor:pointer;display:none;margin:0;width:calc(1em + .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:#212529;opacity:.8}.aa-Autocomplete .aa-Form .aa-InputWrapperSuffix .aa-CopyButton[hidden],.aa-DetachedFormContainer .aa-Form .aa-InputWrapperSuffix .aa-CopyButton[hidden]{display:none}.aa-PanelLayout:empty{display:none}.quarto-search-no-results.no-query{display:none}.aa-Source:has(.no-query){display:none}#quarto-search-results .aa-Panel{border:solid #dee2e6 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:#0d6efd}.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:#0d6efd}.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:#3586fd}.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:#212529}.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:#e1edff}.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:#212529}.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:#dee2e6;color:#212529}.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:0em}.aa-DetachedOverlay .aa-Item .search-item .search-result-crumbs,#quarto-search-results .aa-Item .search-item .search-result-crumbs{white-space:nowrap;text-overflow:ellipsis;font-size:.8em;font-weight:300;margin-right:1em}.aa-DetachedOverlay .aa-Item .search-item .search-result-crumbs:not(.search-result-crumbs-wrap),#quarto-search-results .aa-Item .search-item .search-result-crumbs:not(.search-result-crumbs-wrap){max-width:30%;margin-left:auto;margin-top:.5em;margin-bottom:.1rem}.aa-DetachedOverlay .aa-Item .search-item .search-result-crumbs.search-result-crumbs-wrap,#quarto-search-results .aa-Item .search-item .search-result-crumbs.search-result-crumbs-wrap{flex-basis:100%;margin-top:0em;margin-bottom:.2em;margin-left:37px}.aa-DetachedOverlay .aa-Item .search-result-title-container,#quarto-search-results .aa-Item .search-result-title-container{font-size:1em;display:flex;flex-wrap:wrap;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:42px}.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 #dee2e6}.aa-DetachedSearchButton{background:none;border:none}.aa-DetachedSearchButton .aa-DetachedSearchButtonPlaceholder{display:none}.navbar .aa-DetachedSearchButton .aa-DetachedSearchButtonIcon{color:#fdfefe}.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(222,226,230,.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 #dee2e6;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:#212529;cursor:pointer;margin:0 0 0 .5em;padding:0 .5em}.aa-DetachedCancelButton:hover,.aa-DetachedCancelButton:focus{box-shadow:rgba(13,110,253,.6) 0 0 0 1px;outline:currentColor none medium}.aa-DetachedContainer--modal{bottom:inherit;height:auto;margin:0 auto;position:absolute;top:100px;border-radius:6px;max-width:850px}@media(max-width: 575.98px){.aa-DetachedContainer--modal{width:100%;top:0px;border-radius:0px;border:none}}.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(33,37,41,.4);position:fixed;left:0;right:0;top:0;margin:0;padding:0;height:100vh;z-index:1100}.quarto-dashboard.nav-fixed.dashboard-sidebar #quarto-content.quarto-dashboard-content{padding:0em}.quarto-dashboard #quarto-content.quarto-dashboard-content{padding:1em}.quarto-dashboard #quarto-content.quarto-dashboard-content>*{padding-top:0}@media(min-width: 576px){.quarto-dashboard{height:100%}}.quarto-dashboard .card.valuebox.bslib-card.bg-primary{background-color:#0d6efd !important}.quarto-dashboard .card.valuebox.bslib-card.bg-secondary{background-color:#6c757d !important}.quarto-dashboard .card.valuebox.bslib-card.bg-success{background-color:#198754 !important}.quarto-dashboard .card.valuebox.bslib-card.bg-info{background-color:#0dcaf0 !important}.quarto-dashboard .card.valuebox.bslib-card.bg-warning{background-color:#ffc107 !important}.quarto-dashboard .card.valuebox.bslib-card.bg-danger{background-color:#dc3545 !important}.quarto-dashboard .card.valuebox.bslib-card.bg-light{background-color:#f8f9fa !important}.quarto-dashboard .card.valuebox.bslib-card.bg-dark{background-color:#212529 !important}.quarto-dashboard.dashboard-fill{display:flex;flex-direction:column}.quarto-dashboard #quarto-appendix{display:none}.quarto-dashboard #quarto-header #quarto-dashboard-header{border-top:solid 1px #6c8fb1;border-bottom:solid 1px #6c8fb1}.quarto-dashboard #quarto-header #quarto-dashboard-header>nav{padding-left:1em;padding-right:1em}.quarto-dashboard #quarto-header #quarto-dashboard-header>nav .navbar-brand-container{padding-left:0}.quarto-dashboard #quarto-header #quarto-dashboard-header .navbar-toggler{margin-right:0}.quarto-dashboard #quarto-header #quarto-dashboard-header .navbar-toggler-icon{height:1em;width:1em;background-image:url('data:image/svg+xml,')}.quarto-dashboard #quarto-header #quarto-dashboard-header .navbar-brand-container{padding-right:1em}.quarto-dashboard #quarto-header #quarto-dashboard-header .navbar-title{font-size:1.1em}.quarto-dashboard #quarto-header #quarto-dashboard-header .navbar-nav{font-size:.9em}.quarto-dashboard #quarto-dashboard-header .navbar{padding:0}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-container{padding-left:1em}.quarto-dashboard #quarto-dashboard-header .navbar.slim .navbar-brand-container .nav-link,.quarto-dashboard #quarto-dashboard-header .navbar.slim .navbar-nav .nav-link{padding:.7em}.quarto-dashboard #quarto-dashboard-header .navbar .quarto-color-scheme-toggle{order:9}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-toggler{margin-left:.5em;order:10}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-nav .nav-link{padding:.5em;height:100%;display:flex;align-items:center}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-nav .active{background-color:#658aae}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-brand-container{padding:.5em .5em .5em 0;display:flex;flex-direction:row;margin-right:2em;align-items:center}@media(max-width: 767.98px){.quarto-dashboard #quarto-dashboard-header .navbar .navbar-brand-container{margin-right:auto}}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-collapse{align-self:stretch}@media(min-width: 768px){.quarto-dashboard #quarto-dashboard-header .navbar .navbar-collapse{order:8}}@media(max-width: 767.98px){.quarto-dashboard #quarto-dashboard-header .navbar .navbar-collapse{order:1000;padding-bottom:.5em}}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-collapse .navbar-nav{align-self:stretch}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-title{font-size:1.25em;line-height:1.1em;display:flex;flex-direction:row;flex-wrap:wrap;align-items:baseline}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-title .navbar-title-text{margin-right:.4em}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-title a{text-decoration:none;color:inherit}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-subtitle,.quarto-dashboard #quarto-dashboard-header .navbar .navbar-author{font-size:.9rem;margin-right:.5em}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-author{margin-left:auto}.quarto-dashboard #quarto-dashboard-header .navbar .navbar-logo{max-height:48px;min-height:30px;object-fit:cover;margin-right:1em}.quarto-dashboard #quarto-dashboard-header .navbar .quarto-dashboard-links{order:9;padding-right:1em}.quarto-dashboard #quarto-dashboard-header .navbar .quarto-dashboard-link-text{margin-left:.25em}.quarto-dashboard #quarto-dashboard-header .navbar .quarto-dashboard-link{padding-right:0em;padding-left:.7em;text-decoration:none;color:#fdfefe}.quarto-dashboard .page-layout-custom .tab-content{padding:0;border:none}.quarto-dashboard-img-contain{height:100%;width:100%;object-fit:contain}@media(max-width: 575.98px){.quarto-dashboard .bslib-grid{grid-template-rows:minmax(1em, max-content) !important}.quarto-dashboard .sidebar-content{height:inherit}.quarto-dashboard .page-layout-custom{min-height:100vh}}.quarto-dashboard.dashboard-toolbar>.page-layout-custom,.quarto-dashboard.dashboard-sidebar>.page-layout-custom{padding:0}.quarto-dashboard .quarto-dashboard-content.quarto-dashboard-pages{padding:0}.quarto-dashboard .callout{margin-bottom:0;margin-top:0}.quarto-dashboard .html-fill-container figure{overflow:hidden}.quarto-dashboard bslib-tooltip .rounded-pill{border:solid #6c757d 1px}.quarto-dashboard bslib-tooltip .rounded-pill .svg{fill:#212529}.quarto-dashboard .tabset .dashboard-card-no-title .nav-tabs{margin-left:0;margin-right:auto}.quarto-dashboard .tabset .tab-content{border:none}.quarto-dashboard .tabset .card-header .nav-link[role=tab]{margin-top:-6px;padding-top:6px;padding-bottom:6px}.quarto-dashboard .card.valuebox,.quarto-dashboard .card.bslib-value-box{min-height:3rem}.quarto-dashboard .card.valuebox .card-body,.quarto-dashboard .card.bslib-value-box .card-body{padding:0}.quarto-dashboard .bslib-value-box .value-box-value{font-size:clamp(.1em,15cqw,5em)}.quarto-dashboard .bslib-value-box .value-box-showcase .bi{font-size:clamp(.1em,max(18cqw,5.2cqh),5em);text-align:center;height:1em}.quarto-dashboard .bslib-value-box .value-box-showcase .bi::before{vertical-align:1em}.quarto-dashboard .bslib-value-box .value-box-area{margin-top:auto;margin-bottom:auto}.quarto-dashboard .card figure.quarto-float{display:flex;flex-direction:column;align-items:center}.quarto-dashboard .dashboard-scrolling{padding:1em}.quarto-dashboard .full-height{height:100%}.quarto-dashboard .showcase-bottom .value-box-grid{display:grid;grid-template-columns:1fr;grid-template-rows:1fr auto;grid-template-areas:"top" "bottom"}.quarto-dashboard .showcase-bottom .value-box-grid .value-box-showcase{grid-area:bottom;padding:0;margin:0}.quarto-dashboard .showcase-bottom .value-box-grid .value-box-showcase i.bi{font-size:4rem}.quarto-dashboard .showcase-bottom .value-box-grid .value-box-area{grid-area:top}.quarto-dashboard .tab-content{margin-bottom:0}.quarto-dashboard .bslib-card .bslib-navs-card-title{justify-content:stretch;align-items:end}.quarto-dashboard .card-header{display:flex;flex-wrap:wrap;justify-content:space-between}.quarto-dashboard .card-header .card-title{display:flex;flex-direction:column;justify-content:center;margin-bottom:0}.quarto-dashboard .tabset .card-toolbar{margin-bottom:1em}.quarto-dashboard .bslib-grid>.bslib-sidebar-layout{border:none;gap:var(--bslib-spacer, 1rem)}.quarto-dashboard .bslib-grid>.bslib-sidebar-layout>.main{padding:0}.quarto-dashboard .bslib-grid>.bslib-sidebar-layout>.sidebar{border-radius:.25rem;border:1px solid rgba(0,0,0,.175)}.quarto-dashboard .bslib-grid>.bslib-sidebar-layout>.collapse-toggle{display:none}@media(max-width: 767.98px){.quarto-dashboard .bslib-grid>.bslib-sidebar-layout{grid-template-columns:1fr;grid-template-rows:max-content 1fr}.quarto-dashboard .bslib-grid>.bslib-sidebar-layout>.main{grid-column:1;grid-row:2}.quarto-dashboard .bslib-grid>.bslib-sidebar-layout .sidebar{grid-column:1;grid-row:1}}.quarto-dashboard .sidebar-right .sidebar{padding-left:2.5em}.quarto-dashboard .sidebar-right .collapse-toggle{left:2px}.quarto-dashboard .quarto-dashboard .sidebar-right button.collapse-toggle:not(.transitioning){left:unset}.quarto-dashboard aside.sidebar{padding-left:1em;padding-right:1em;background-color:rgba(52,58,64,.25);color:#212529}.quarto-dashboard .bslib-sidebar-layout>div.main{padding:.7em}.quarto-dashboard .bslib-sidebar-layout button.collapse-toggle{margin-top:.3em}.quarto-dashboard .bslib-sidebar-layout .collapse-toggle{top:0}.quarto-dashboard .bslib-sidebar-layout.sidebar-collapsed:not(.transitioning):not(.sidebar-right) .collapse-toggle{left:2px}.quarto-dashboard .sidebar>section>.h3:first-of-type{margin-top:0em}.quarto-dashboard .sidebar .h3,.quarto-dashboard .sidebar .h4,.quarto-dashboard .sidebar .h5,.quarto-dashboard .sidebar .h6{margin-top:.5em}.quarto-dashboard .sidebar form{flex-direction:column;align-items:start;margin-bottom:1em}.quarto-dashboard .sidebar form div[class*=oi-][class$=-input]{flex-direction:column}.quarto-dashboard .sidebar form[class*=oi-][class$=-toggle]{flex-direction:row-reverse;align-items:center;justify-content:start}.quarto-dashboard .sidebar form input[type=range]{margin-top:.5em;margin-right:.8em;margin-left:1em}.quarto-dashboard .sidebar label{width:fit-content}.quarto-dashboard .sidebar .card-body{margin-bottom:2em}.quarto-dashboard .sidebar .shiny-input-container{margin-bottom:1em}.quarto-dashboard .sidebar .shiny-options-group{margin-top:0}.quarto-dashboard .sidebar .control-label{margin-bottom:.3em}.quarto-dashboard .card .card-body .quarto-layout-row{align-items:stretch}.quarto-dashboard .toolbar{font-size:.9em;display:flex;flex-direction:row;border-top:solid 1px #c7c9cd;padding:1em;flex-wrap:wrap;background-color:rgba(52,58,64,.25)}.quarto-dashboard .toolbar .cell-output-display{display:flex}.quarto-dashboard .toolbar .shiny-input-container{padding-bottom:.5em;margin-bottom:.5em;width:inherit}.quarto-dashboard .toolbar .shiny-input-container>.checkbox:first-child{margin-top:6px}.quarto-dashboard .toolbar>*:last-child{margin-right:0}.quarto-dashboard .toolbar>*>*{margin-right:1em;align-items:baseline}.quarto-dashboard .toolbar>*>*>a{text-decoration:none;margin-top:auto;margin-bottom:auto}.quarto-dashboard .toolbar .shiny-input-container{padding-bottom:0;margin-bottom:0}.quarto-dashboard .toolbar .shiny-input-container>*{flex-shrink:0;flex-grow:0}.quarto-dashboard .toolbar .form-group.shiny-input-container:not([role=group])>label{margin-bottom:0}.quarto-dashboard .toolbar .shiny-input-container.no-baseline{align-items:start;padding-top:6px}.quarto-dashboard .toolbar .shiny-input-container{display:flex;align-items:baseline}.quarto-dashboard .toolbar .shiny-input-container label{padding-right:.4em}.quarto-dashboard .toolbar .shiny-input-container .bslib-input-switch{margin-top:6px}.quarto-dashboard .toolbar input[type=text]{line-height:1;width:inherit}.quarto-dashboard .toolbar .input-daterange{width:inherit}.quarto-dashboard .toolbar .input-daterange input[type=text]{height:2.4em;width:10em}.quarto-dashboard .toolbar .input-daterange .input-group-addon{height:auto;padding:0;margin-left:-5px !important;margin-right:-5px}.quarto-dashboard .toolbar .input-daterange .input-group-addon .input-group-text{padding-top:0;padding-bottom:0;height:100%}.quarto-dashboard .toolbar span.irs.irs--shiny{width:10em}.quarto-dashboard .toolbar span.irs.irs--shiny .irs-line{top:9px}.quarto-dashboard .toolbar span.irs.irs--shiny .irs-min,.quarto-dashboard .toolbar span.irs.irs--shiny .irs-max,.quarto-dashboard .toolbar span.irs.irs--shiny .irs-from,.quarto-dashboard .toolbar span.irs.irs--shiny .irs-to,.quarto-dashboard .toolbar span.irs.irs--shiny .irs-single{top:20px}.quarto-dashboard .toolbar span.irs.irs--shiny .irs-bar{top:8px}.quarto-dashboard .toolbar span.irs.irs--shiny .irs-handle{top:0px}.quarto-dashboard .toolbar .shiny-input-checkboxgroup>label{margin-top:6px}.quarto-dashboard .toolbar .shiny-input-checkboxgroup>.shiny-options-group{margin-top:0;align-items:baseline}.quarto-dashboard .toolbar .shiny-input-radiogroup>label{margin-top:6px}.quarto-dashboard .toolbar .shiny-input-radiogroup>.shiny-options-group{align-items:baseline;margin-top:0}.quarto-dashboard .toolbar .shiny-input-radiogroup>.shiny-options-group>.radio{margin-right:.3em}.quarto-dashboard .toolbar .form-select{padding-top:.2em;padding-bottom:.2em}.quarto-dashboard .toolbar .shiny-input-select{min-width:6em}.quarto-dashboard .toolbar div.checkbox{margin-bottom:0px}.quarto-dashboard .toolbar>.checkbox:first-child{margin-top:6px}.quarto-dashboard .toolbar form{width:fit-content}.quarto-dashboard .toolbar form label{padding-top:.2em;padding-bottom:.2em;width:fit-content}.quarto-dashboard .toolbar form input[type=date]{width:fit-content}.quarto-dashboard .toolbar form input[type=color]{width:3em}.quarto-dashboard .toolbar form button{padding:.4em}.quarto-dashboard .toolbar form select{width:fit-content}.quarto-dashboard .toolbar>*{font-size:.9em;flex-grow:0}.quarto-dashboard .toolbar .shiny-input-container label{margin-bottom:1px}.quarto-dashboard .toolbar-bottom{margin-top:1em;margin-bottom:0 !important;order:2}.quarto-dashboard .quarto-dashboard-content>.dashboard-toolbar-container>.toolbar-content>.tab-content>.tab-pane>*:not(.bslib-sidebar-layout){padding:1em}.quarto-dashboard .quarto-dashboard-content>.dashboard-toolbar-container>.toolbar-content>*:not(.tab-content){padding:1em}.quarto-dashboard .quarto-dashboard-content>.tab-content>.dashboard-page>.dashboard-toolbar-container>.toolbar-content,.quarto-dashboard .quarto-dashboard-content>.tab-content>.dashboard-page:not(.dashboard-sidebar-container)>*:not(.dashboard-toolbar-container){padding:1em}.quarto-dashboard .toolbar-content{padding:0}.quarto-dashboard .quarto-dashboard-content.quarto-dashboard-pages .tab-pane>.dashboard-toolbar-container .toolbar{border-radius:0;margin-bottom:0}.quarto-dashboard .dashboard-toolbar-container.toolbar-toplevel .toolbar{border-bottom:1px solid rgba(0,0,0,.175)}.quarto-dashboard .dashboard-toolbar-container.toolbar-toplevel .toolbar-bottom{margin-top:0}.quarto-dashboard .dashboard-toolbar-container:not(.toolbar-toplevel) .toolbar{margin-bottom:1em;border-top:none;border-radius:.25rem;border:1px solid rgba(0,0,0,.175)}.quarto-dashboard .vega-embed.has-actions details{width:1.7em;height:2em;position:absolute !important;top:0;right:0}.quarto-dashboard .dashboard-toolbar-container{padding:0}.quarto-dashboard .card .card-header p:last-child,.quarto-dashboard .card .card-footer p:last-child{margin-bottom:0}.quarto-dashboard .card .card-body>.h4:first-child{margin-top:0}.quarto-dashboard .card .card-body{z-index:1000}@media(max-width: 767.98px){.quarto-dashboard .card .card-body .itables div.dataTables_wrapper div.dataTables_length,.quarto-dashboard .card .card-body .itables div.dataTables_wrapper div.dataTables_info,.quarto-dashboard .card .card-body .itables div.dataTables_wrapper div.dataTables_paginate{text-align:initial}.quarto-dashboard .card .card-body .itables div.dataTables_wrapper div.dataTables_filter{text-align:right}.quarto-dashboard .card .card-body .itables div.dataTables_wrapper div.dataTables_paginate ul.pagination{justify-content:initial}}.quarto-dashboard .card .card-body .itables .dataTables_wrapper{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;padding-top:0}.quarto-dashboard .card .card-body .itables .dataTables_wrapper table{flex-shrink:0}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dt-buttons{margin-bottom:.5em;margin-left:auto;width:fit-content;float:right}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dt-buttons.btn-group{background:#fff;border:none}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dt-buttons .btn-secondary{background-color:#fff;background-image:none;border:solid #dee2e6 1px;padding:.2em .7em}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dt-buttons .btn span{font-size:.8em;color:#212529}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_info{margin-left:.5em;margin-bottom:.5em;padding-top:0}@media(min-width: 768px){.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_info{font-size:.875em}}@media(max-width: 767.98px){.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_info{font-size:.8em}}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_filter{margin-bottom:.5em;font-size:.875em}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_filter input[type=search]{padding:1px 5px 1px 5px;font-size:.875em}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_length{flex-basis:1 1 50%;margin-bottom:.5em;font-size:.875em}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_length select{padding:.4em 3em .4em .5em;font-size:.875em;margin-left:.2em;margin-right:.2em}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_paginate{flex-shrink:0}@media(min-width: 768px){.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_paginate{margin-left:auto}}.quarto-dashboard .card .card-body .itables .dataTables_wrapper .dataTables_paginate ul.pagination .paginate_button .page-link{font-size:.8em}.quarto-dashboard .card .card-footer{font-size:.9em}.quarto-dashboard .card .card-toolbar{display:flex;flex-grow:1;flex-direction:row;width:100%;flex-wrap:wrap}.quarto-dashboard .card .card-toolbar>*{font-size:.8em;flex-grow:0}.quarto-dashboard .card .card-toolbar>.card-title{font-size:1em;flex-grow:1;align-self:flex-start;margin-top:.1em}.quarto-dashboard .card .card-toolbar .cell-output-display{display:flex}.quarto-dashboard .card .card-toolbar .shiny-input-container{padding-bottom:.5em;margin-bottom:.5em;width:inherit}.quarto-dashboard .card .card-toolbar .shiny-input-container>.checkbox:first-child{margin-top:6px}.quarto-dashboard .card .card-toolbar>*:last-child{margin-right:0}.quarto-dashboard .card .card-toolbar>*>*{margin-right:1em;align-items:baseline}.quarto-dashboard .card .card-toolbar>*>*>a{text-decoration:none;margin-top:auto;margin-bottom:auto}.quarto-dashboard .card .card-toolbar form{width:fit-content}.quarto-dashboard .card .card-toolbar form label{padding-top:.2em;padding-bottom:.2em;width:fit-content}.quarto-dashboard .card .card-toolbar form input[type=date]{width:fit-content}.quarto-dashboard .card .card-toolbar form input[type=color]{width:3em}.quarto-dashboard .card .card-toolbar form button{padding:.4em}.quarto-dashboard .card .card-toolbar form select{width:fit-content}.quarto-dashboard .card .card-toolbar .cell-output-display{display:flex}.quarto-dashboard .card .card-toolbar .shiny-input-container{padding-bottom:.5em;margin-bottom:.5em;width:inherit}.quarto-dashboard .card .card-toolbar .shiny-input-container>.checkbox:first-child{margin-top:6px}.quarto-dashboard .card .card-toolbar>*:last-child{margin-right:0}.quarto-dashboard .card .card-toolbar>*>*{margin-right:1em;align-items:baseline}.quarto-dashboard .card .card-toolbar>*>*>a{text-decoration:none;margin-top:auto;margin-bottom:auto}.quarto-dashboard .card .card-toolbar .shiny-input-container{padding-bottom:0;margin-bottom:0}.quarto-dashboard .card .card-toolbar .shiny-input-container>*{flex-shrink:0;flex-grow:0}.quarto-dashboard .card .card-toolbar .form-group.shiny-input-container:not([role=group])>label{margin-bottom:0}.quarto-dashboard .card .card-toolbar .shiny-input-container.no-baseline{align-items:start;padding-top:6px}.quarto-dashboard .card .card-toolbar .shiny-input-container{display:flex;align-items:baseline}.quarto-dashboard .card .card-toolbar .shiny-input-container label{padding-right:.4em}.quarto-dashboard .card .card-toolbar .shiny-input-container .bslib-input-switch{margin-top:6px}.quarto-dashboard .card .card-toolbar input[type=text]{line-height:1;width:inherit}.quarto-dashboard .card .card-toolbar .input-daterange{width:inherit}.quarto-dashboard .card .card-toolbar .input-daterange input[type=text]{height:2.4em;width:10em}.quarto-dashboard .card .card-toolbar .input-daterange .input-group-addon{height:auto;padding:0;margin-left:-5px !important;margin-right:-5px}.quarto-dashboard .card .card-toolbar .input-daterange .input-group-addon .input-group-text{padding-top:0;padding-bottom:0;height:100%}.quarto-dashboard .card .card-toolbar span.irs.irs--shiny{width:10em}.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-line{top:9px}.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-min,.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-max,.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-from,.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-to,.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-single{top:20px}.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-bar{top:8px}.quarto-dashboard .card .card-toolbar span.irs.irs--shiny .irs-handle{top:0px}.quarto-dashboard .card .card-toolbar .shiny-input-checkboxgroup>label{margin-top:6px}.quarto-dashboard .card .card-toolbar .shiny-input-checkboxgroup>.shiny-options-group{margin-top:0;align-items:baseline}.quarto-dashboard .card .card-toolbar .shiny-input-radiogroup>label{margin-top:6px}.quarto-dashboard .card .card-toolbar .shiny-input-radiogroup>.shiny-options-group{align-items:baseline;margin-top:0}.quarto-dashboard .card .card-toolbar .shiny-input-radiogroup>.shiny-options-group>.radio{margin-right:.3em}.quarto-dashboard .card .card-toolbar .form-select{padding-top:.2em;padding-bottom:.2em}.quarto-dashboard .card .card-toolbar .shiny-input-select{min-width:6em}.quarto-dashboard .card .card-toolbar div.checkbox{margin-bottom:0px}.quarto-dashboard .card .card-toolbar>.checkbox:first-child{margin-top:6px}.quarto-dashboard .card-body>table>thead{border-top:none}.quarto-dashboard .card-body>.table>:not(caption)>*>*{background-color:#fff}.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:#fff;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 p.card-img-top>img{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 .listing-item-img-placeholder{background-color:rgba(52,58,64,.25);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;word-break:keep-all}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 .body pre code{white-space:pre-wrap}div.quarto-post a{color:#212529;text-decoration:none}div.quarto-post .metadata{display:flex;flex-direction:column;font-size:.8em;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";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:#4e5862;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:#0d6efd}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:#4e5862;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:#0d6efd}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:#4e5862;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:#0d6efd}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:#4e5862;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:#0d6efd}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:#4e5862;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:#0d6efd}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;border:solid 1px #dee2e6;border-radius:.25rem;color:#212529;font-size:.875rem}.tippy-box[data-theme~=quarto]>.tippy-backdrop{background-color:#fff}.tippy-box[data-theme~=quarto]>.tippy-arrow:after,.tippy-box[data-theme~=quarto]>.tippy-svg-arrow:after{content:"";position:absolute;z-index:-1}.tippy-box[data-theme~=quarto]>.tippy-arrow:after{border-color:rgba(0,0,0,0);border-style:solid}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-6px}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-6px}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-6px}.tippy-box[data-placement^=left]>.tippy-arrow:before{right:-6px}.tippy-box[data-theme~=quarto][data-placement^=top]>.tippy-arrow:before{border-top-color:#fff}.tippy-box[data-theme~=quarto][data-placement^=top]>.tippy-arrow:after{border-top-color:#dee2e6;border-width:7px 7px 0;top:17px;left:1px}.tippy-box[data-theme~=quarto][data-placement^=top]>.tippy-svg-arrow>svg{top:16px}.tippy-box[data-theme~=quarto][data-placement^=top]>.tippy-svg-arrow:after{top:17px}.tippy-box[data-theme~=quarto][data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:#fff;bottom:16px}.tippy-box[data-theme~=quarto][data-placement^=bottom]>.tippy-arrow:after{border-bottom-color:#dee2e6;border-width:0 7px 7px;bottom:17px;left:1px}.tippy-box[data-theme~=quarto][data-placement^=bottom]>.tippy-svg-arrow>svg{bottom:15px}.tippy-box[data-theme~=quarto][data-placement^=bottom]>.tippy-svg-arrow:after{bottom:17px}.tippy-box[data-theme~=quarto][data-placement^=left]>.tippy-arrow:before{border-left-color:#fff}.tippy-box[data-theme~=quarto][data-placement^=left]>.tippy-arrow:after{border-left-color:#dee2e6;border-width:7px 0 7px 7px;left:17px;top:1px}.tippy-box[data-theme~=quarto][data-placement^=left]>.tippy-svg-arrow>svg{left:11px}.tippy-box[data-theme~=quarto][data-placement^=left]>.tippy-svg-arrow:after{left:12px}.tippy-box[data-theme~=quarto][data-placement^=right]>.tippy-arrow:before{border-right-color:#fff;right:16px}.tippy-box[data-theme~=quarto][data-placement^=right]>.tippy-arrow:after{border-width:7px 7px 7px 0;right:17px;top:1px;border-right-color:#dee2e6}.tippy-box[data-theme~=quarto][data-placement^=right]>.tippy-svg-arrow>svg{right:11px}.tippy-box[data-theme~=quarto][data-placement^=right]>.tippy-svg-arrow:after{right:12px}.tippy-box[data-theme~=quarto]>.tippy-svg-arrow{fill:#212529}.tippy-box[data-theme~=quarto]>.tippy-svg-arrow:after{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMCA2czEuNzk2LS4wMTMgNC42Ny0zLjYxNUM1Ljg1MS45IDYuOTMuMDA2IDggMGMxLjA3LS4wMDYgMi4xNDguODg3IDMuMzQzIDIuMzg1QzE0LjIzMyA2LjAwNSAxNiA2IDE2IDZIMHoiIGZpbGw9InJnYmEoMCwgOCwgMTYsIDAuMikiLz48L3N2Zz4=);background-size:16px 6px;width:16px;height:6px}.top-right{position:absolute;top:1em;right:1em}.visually-hidden{border:0;clip:rect(0 0 0 0);height:auto;margin:0;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap}.hidden{display:none !important}.zindex-bottom{z-index:-1 !important}figure.figure{display:block}.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: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,.quarto-figure-left>figure>div{text-align:left}.quarto-figure-center>figure>p,.quarto-figure-center>figure>div{text-align:center}.quarto-figure-right>figure>p,.quarto-figure-right>figure>div{text-align:right}.quarto-figure>figure>div.cell-annotation,.quarto-figure>figure>div code{text-align:left}figure>p:empty{display:none}figure>p:first-child{margin-top:0;margin-bottom:0}figure>figcaption.quarto-float-caption-bottom{margin-bottom:.5em}figure>figcaption.quarto-float-caption-top{margin-top:.5em}div[id^=tbl-]{position:relative}.quarto-figure>.anchorjs-link{position:absolute;top:.6em;right:.5em}div[id^=tbl-]>.anchorjs-link{position:absolute;top:.7em;right:.3em}.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.table{margin-top:.5rem;margin-bottom:.5rem}caption,.table-caption{padding-top:.5rem;padding-bottom:.5rem;text-align:center}figure.quarto-float-tbl figcaption.quarto-float-caption-top{margin-top:.5rem;margin-bottom:.25rem;text-align:center}figure.quarto-float-tbl figcaption.quarto-float-caption-bottom{padding-top:.25rem;margin-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}.footnote-back{margin-left:.2em}.tippy-content{overflow-x:auto}.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),.knitsql-table: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}a{text-underline-offset:3px}div.ansi-escaped-output{font-family:monospace;display:block}/*! +* +* ansi colors from IPython notebook's +* +* we also add `bright-[color]-` synonyms for the `-[color]-intense` classes since +* that seems to be what ansi_up emits +* +*/.ansi-black-fg{color:#3e424d}.ansi-black-bg{background-color:#3e424d}.ansi-black-intense-black,.ansi-bright-black-fg{color:#282c36}.ansi-black-intense-black,.ansi-bright-black-bg{background-color:#282c36}.ansi-red-fg{color:#e75c58}.ansi-red-bg{background-color:#e75c58}.ansi-red-intense-red,.ansi-bright-red-fg{color:#b22b31}.ansi-red-intense-red,.ansi-bright-red-bg{background-color:#b22b31}.ansi-green-fg{color:#00a250}.ansi-green-bg{background-color:#00a250}.ansi-green-intense-green,.ansi-bright-green-fg{color:#007427}.ansi-green-intense-green,.ansi-bright-green-bg{background-color:#007427}.ansi-yellow-fg{color:#ddb62b}.ansi-yellow-bg{background-color:#ddb62b}.ansi-yellow-intense-yellow,.ansi-bright-yellow-fg{color:#b27d12}.ansi-yellow-intense-yellow,.ansi-bright-yellow-bg{background-color:#b27d12}.ansi-blue-fg{color:#208ffb}.ansi-blue-bg{background-color:#208ffb}.ansi-blue-intense-blue,.ansi-bright-blue-fg{color:#0065ca}.ansi-blue-intense-blue,.ansi-bright-blue-bg{background-color:#0065ca}.ansi-magenta-fg{color:#d160c4}.ansi-magenta-bg{background-color:#d160c4}.ansi-magenta-intense-magenta,.ansi-bright-magenta-fg{color:#a03196}.ansi-magenta-intense-magenta,.ansi-bright-magenta-bg{background-color:#a03196}.ansi-cyan-fg{color:#60c6c8}.ansi-cyan-bg{background-color:#60c6c8}.ansi-cyan-intense-cyan,.ansi-bright-cyan-fg{color:#258f8f}.ansi-cyan-intense-cyan,.ansi-bright-cyan-bg{background-color:#258f8f}.ansi-white-fg{color:#c5c1b4}.ansi-white-bg{background-color:#c5c1b4}.ansi-white-intense-white,.ansi-bright-white-fg{color:#a1a6b2}.ansi-white-intense-white,.ansi-bright-white-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: #ffffff;--quarto-body-color: #212529;--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:rgba(0,0,0,0);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:rgba(0,0,0,0);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:rgba(0,0,0,0)}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%}.code-annotation-tip-content{word-wrap:break-word}.code-annotation-container-hidden{display:none !important}dl.code-annotation-container-grid{display:grid;grid-template-columns:min-content auto}dl.code-annotation-container-grid dt{grid-column:1}dl.code-annotation-container-grid dd{grid-column:2}pre.sourceCode.code-annotation-code{padding-right:0}code.sourceCode .code-annotation-anchor{z-index:100;position:relative;float:right;background-color:rgba(0,0,0,0)}input[type=checkbox]{margin-right:.5ch}:root{--mermaid-bg-color: #ffffff;--mermaid-edge-color: #6c757d;--mermaid-node-fg-color: #212529;--mermaid-fg-color: #212529;--mermaid-fg-color--lighter: #383f45;--mermaid-fg-color--lightest: #4e5862;--mermaid-font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Noto Sans, Liberation Sans, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;--mermaid-label-bg-color: #ffffff;--mermaid-label-fg-color: #0d6efd;--mermaid-node-bg-color: rgba(13, 110, 253, 0.1);--mermaid-node-fg-color: #212529}@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:rgba(0,0,0,0);z-index:3}.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}ul>li:not(:has(>p))>ul,ol>li:not(:has(>p))>ul,ul>li:not(:has(>p))>ol,ol>li:not(:has(>p))>ol{margin-bottom:0}ul>li:not(:has(>p))>ul>li:has(>p),ol>li:not(:has(>p))>ul>li:has(>p),ul>li:not(:has(>p))>ol>li:has(>p),ol>li:not(:has(>p))>ol>li:has(>p){margin-top:1rem}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}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(850px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [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] minmax(50px, 100px) [page-start-inset] 50px [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(500px, calc(850px - 3em)) [body-content-end] 3em [body-end] 50px [body-end-outset] minmax(0px, 250px) [page-end-inset] minmax(50px, 100px) [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(800px - 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(800px - 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(50px, 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, 200px) [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(800px - 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(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.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(800px - 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(1250px - 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(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: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(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.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] 1.5em [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] 4fr [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] 50px [body-end-outset] minmax(25px, 50px) [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 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] 4fr [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 .page-columns.page-full>*,.page-columns.column-page-left>*{grid-column:page-start/body-content-end}.page-columns.column-page-right .page-columns.page-full>*,.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;opacity:.999}.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;opacity:.999}.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;opacity:.999}.page-columns .column-body-outset-right table{background:#fff}.page-columns .column-page{grid-column:page-start/page-end;z-index:998;opacity:.999}.page-columns .column-page table{background:#fff}.page-columns .column-page-inset{grid-column:page-start-inset/page-end-inset;z-index:998;opacity:.999}.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;opacity:.999}.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;opacity:.999}.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;opacity:.999}.page-columns .column-page-left table{background:#fff}.page-columns .column-page-right{grid-column:body-content-start/page-end;z-index:998;opacity:.999}.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,#quarto-content.page-columns #quarto-margin-sidebar.collapsing,#quarto-content.page-columns #quarto-sidebar.collapsing{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;opacity:.999}.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;opacity:.999}.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;opacity:.999}.page-columns .column-screen-inset-right table{background:#fff}.page-columns .column-screen{grid-column:screen-start/screen-end;z-index:998;opacity:.999}.page-columns .column-screen table{background:#fff}.page-columns .column-screen-left{grid-column:screen-start/body-content-end;z-index:998;opacity:.999}.page-columns .column-screen-left table{background:#fff}.page-columns .column-screen-right{grid-column:body-content-start/screen-end;z-index:998;opacity:.999}.page-columns .column-screen-right table{background:#fff}.page-columns .column-screen-inset-shaded{grid-column:screen-start/screen-end;padding:1em;background:#f8f9fa;z-index:998;opacity:.999;margin-bottom:1em}.zindex-content{z-index:998;opacity:.999}.zindex-modal{z-index:1055;opacity:.999}.zindex-over-content{z-index:999;opacity:.999}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:not(.footnotes):not(.sidebar),.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:not(.footnotes):not(.sidebar),.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:not(.footnotes):not(.sidebar),.page-columns.toc-left .column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;opacity:.999}.page-columns.toc-left .no-row-height{height:initial;overflow:initial}}@media(max-width: 767.98px){.margin-caption,div.aside,aside:not(.footnotes):not(.sidebar),.column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;opacity:.999}.no-row-height{height:initial;overflow:initial}#quarto-margin-sidebar{display:none}#quarto-sidebar-toc-left{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{color:inherit;margin-top:2rem;margin-bottom:1rem;font-weight:600}h1.title,.title.h1{margin-top:0}main.content>section:first-of-type>h2:first-child,main.content>section:first-of-type>.h2:first-child{margin-top:0}h2,.h2{border-bottom:1px solid #dee2e6;padding-bottom:.5rem}h3,.h3{font-weight:600}h3,.h3,h4,.h4{opacity:.9;margin-top:1.5rem}h5,.h5,h6,.h6{opacity:.9}.header-section-number{color:#5a6570}.nav-link.active .header-section-number{color:inherit}mark,.mark{padding:0em}.panel-caption,.figure-caption,.subfigure-caption,.table-caption,figcaption,caption{font-size:.9rem;color:#5a6570}.quarto-layout-cell[data-ref-parent] caption{color:#5a6570}.column-margin figcaption,.margin-caption,div.aside,aside,.column-margin{color:#5a6570;font-size:.825rem}.panel-caption.margin-caption{text-align:inherit}.column-margin.column-container p{margin-bottom:0}.column-margin.column-container>*:not(.collapse):first-child{padding-bottom:.5em;display:block}.column-margin.column-container>*:not(.collapse):not(:first-child){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}}.nav-tabs .nav-item{margin-top:1px;cursor:pointer}.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:0}.tab-pane>p:nth-child(1){padding-top:0}.tab-pane>p:last-child{margin-bottom:0}.tab-pane>pre:last-child{margin-bottom:0}.tab-content>.tab-pane:not(.active){display:none !important}div.sourceCode{background-color:rgba(233,236,239,.65);border:1px solid rgba(233,236,239,.65);border-radius:.25rem}pre.sourceCode{background-color:rgba(0,0,0,0)}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:#5a6570}.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 pre code:not(.sourceCode),li pre code:not(.sourceCode),pre code:not(.sourceCode){background-color:initial}p code:not(.sourceCode),li code:not(.sourceCode),td code:not(.sourceCode){background-color:#f8f9fa;padding:.2em}nav p code:not(.sourceCode),nav li code:not(.sourceCode),nav td code:not(.sourceCode){background-color:rgba(0,0,0,0);padding:0}td code:not(.sourceCode){white-space:pre-wrap}#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:rgba(0,0,0,0);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.quarto-banner-title-block-sidebar>*{padding-top:1.65em}figure .quarto-notebook-link{margin-top:.5em}.quarto-notebook-link{font-size:.75em;color:#6c757d;margin-bottom:1em;text-decoration:none;display:block}.quarto-notebook-link:hover{text-decoration:underline;color:#0d6efd}.quarto-notebook-link::before{display:inline-block;height:.75rem;width:.75rem;margin-bottom:0em;margin-right:.25em;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:.75rem .75rem}.toc-actions i.bi,.quarto-code-links i.bi,.quarto-other-links i.bi,.quarto-alternate-notebooks i.bi,.quarto-alternate-formats i.bi{margin-right:.4em;font-size:.8rem}.quarto-other-links-text-target .quarto-code-links i.bi,.quarto-other-links-text-target .quarto-other-links i.bi{margin-right:.2em}.quarto-other-formats-text-target .quarto-alternate-formats i.bi{margin-right:.1em}.toc-actions i.bi.empty,.quarto-code-links i.bi.empty,.quarto-other-links i.bi.empty,.quarto-alternate-notebooks i.bi.empty,.quarto-alternate-formats i.bi.empty{padding-left:1em}.quarto-notebook h2,.quarto-notebook .h2{border-bottom:none}.quarto-notebook .cell-container{display:flex}.quarto-notebook .cell-container .cell{flex-grow:4}.quarto-notebook .cell-container .cell-decorator{padding-top:1.5em;padding-right:1em;text-align:right}.quarto-notebook .cell-container.code-fold .cell-decorator{padding-top:3em}.quarto-notebook .cell-code code{white-space:pre-wrap}.quarto-notebook .cell .cell-output-stderr pre code,.quarto-notebook .cell .cell-output-stdout pre code{white-space:pre-wrap;overflow-wrap:anywhere}.toc-actions,.quarto-alternate-formats,.quarto-other-links,.quarto-code-links,.quarto-alternate-notebooks{padding-left:0em}.sidebar .toc-actions a,.sidebar .quarto-alternate-formats a,.sidebar .quarto-other-links a,.sidebar .quarto-code-links a,.sidebar .quarto-alternate-notebooks a,.sidebar nav[role=doc-toc] a{text-decoration:none}.sidebar .toc-actions a:hover,.sidebar .quarto-other-links a:hover,.sidebar .quarto-code-links a:hover,.sidebar .quarto-alternate-formats a:hover,.sidebar .quarto-alternate-notebooks a:hover{color:#0d6efd}.sidebar .toc-actions h2,.sidebar .toc-actions .h2,.sidebar .quarto-code-links h2,.sidebar .quarto-code-links .h2,.sidebar .quarto-other-links h2,.sidebar .quarto-other-links .h2,.sidebar .quarto-alternate-notebooks h2,.sidebar .quarto-alternate-notebooks .h2,.sidebar .quarto-alternate-formats h2,.sidebar .quarto-alternate-formats .h2,.sidebar nav[role=doc-toc]>h2,.sidebar nav[role=doc-toc]>.h2{font-weight:500;margin-bottom:.2rem;margin-top:.3rem;font-family:inherit;border-bottom:0;padding-bottom:0;padding-top:0px}.sidebar .toc-actions>h2,.sidebar .toc-actions>.h2,.sidebar .quarto-code-links>h2,.sidebar .quarto-code-links>.h2,.sidebar .quarto-other-links>h2,.sidebar .quarto-other-links>.h2,.sidebar .quarto-alternate-notebooks>h2,.sidebar .quarto-alternate-notebooks>.h2,.sidebar .quarto-alternate-formats>h2,.sidebar .quarto-alternate-formats>.h2{font-size:.8rem}.sidebar nav[role=doc-toc]>h2,.sidebar nav[role=doc-toc]>.h2{font-size:.875rem}.sidebar nav[role=doc-toc]>ul a{border-left:1px solid #e9ecef;padding-left:.6rem}.sidebar .toc-actions h2>ul a,.sidebar .toc-actions .h2>ul a,.sidebar .quarto-code-links h2>ul a,.sidebar .quarto-code-links .h2>ul a,.sidebar .quarto-other-links h2>ul a,.sidebar .quarto-other-links .h2>ul a,.sidebar .quarto-alternate-notebooks h2>ul a,.sidebar .quarto-alternate-notebooks .h2>ul a,.sidebar .quarto-alternate-formats h2>ul a,.sidebar .quarto-alternate-formats .h2>ul a{border-left:none;padding-left:.6rem}.sidebar .toc-actions ul a:empty,.sidebar .quarto-code-links ul a:empty,.sidebar .quarto-other-links ul a:empty,.sidebar .quarto-alternate-notebooks ul a:empty,.sidebar .quarto-alternate-formats ul a:empty,.sidebar nav[role=doc-toc]>ul a:empty{display:none}.sidebar .toc-actions ul,.sidebar .quarto-code-links ul,.sidebar .quarto-other-links ul,.sidebar .quarto-alternate-notebooks ul,.sidebar .quarto-alternate-formats ul{padding-left:0;list-style:none}.sidebar nav[role=doc-toc] ul{list-style:none;padding-left:0;list-style:none}.sidebar nav[role=doc-toc]>ul{margin-left:.45em}.quarto-margin-sidebar nav[role=doc-toc]{padding-left:.5em}.sidebar .toc-actions>ul,.sidebar .quarto-code-links>ul,.sidebar .quarto-other-links>ul,.sidebar .quarto-alternate-notebooks>ul,.sidebar .quarto-alternate-formats>ul{font-size:.8rem}.sidebar nav[role=doc-toc]>ul{font-size:.875rem}.sidebar .toc-actions ul li a,.sidebar .quarto-code-links ul li a,.sidebar .quarto-other-links ul li a,.sidebar .quarto-alternate-notebooks ul li a,.sidebar .quarto-alternate-formats ul li a,.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>a.active,.sidebar nav[role=doc-toc] ul>li>ul>li>a.active{border-left:1px solid #0d6efd;color:#0d6efd !important}.sidebar nav[role=doc-toc] ul>li>a:hover,.sidebar nav[role=doc-toc] ul>li>ul>li>a:hover{color:#0d6efd !important}kbd,.kbd{color:#212529;background-color:#f8f9fa;border:1px solid;border-radius:5px;border-color:#dee2e6}.quarto-appendix-contents div.hanging-indent{margin-left:0em}.quarto-appendix-contents div.hanging-indent div.csl-entry{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}.callout{margin-top:1.25rem;margin-bottom:1.25rem;border-radius:.25rem;overflow-wrap:break-word}.callout .callout-title-container{overflow-wrap:anywhere}.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:not(.no-icon).callout-titled.callout-style-simple .callout-body{padding-left:1.6em}.callout.callout-titled>.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 .callout-body{padding-left:.5em;padding-right:.5em}.callout.callout-style-default .callout-body>:first-child{padding-top:.5rem;margin-top:0}.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-titled .callout-body>:last-child:not(.sourceCode),.callout.callout-titled .callout-body>div>:last-child:not(.sourceCode){padding-bottom:.5rem;margin-bottom:0}.callout:not(.callout-titled) .callout-body>:first-child,.callout:not(.callout-titled) .callout-body>div>:first-child{margin-top:.25rem}.callout:not(.callout-titled) .callout-body>:last-child,.callout:not(.callout-titled) .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-title-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:#0d6efd}div.callout-note.callout-style-default>.callout-header{background-color:#e7f1ff}div.callout-note:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note.callout-titled .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:#198754}div.callout-tip.callout-style-default>.callout-header{background-color:#e8f3ee}div.callout-tip:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip.callout-titled .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:#ffc107}div.callout-warning.callout-style-default>.callout-header{background-color:#fff9e6}div.callout-warning:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning.callout-titled .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-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution.callout-titled .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:#dc3545}div.callout-important.callout-style-default>.callout-header{background-color:#fcebec}div.callout-important:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-important.callout-titled .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}.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}.sidebar-navigation{padding-left:20px}.navbar{background-color:#517699;color:#fdfefe}.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:#212529}.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}@media(max-width: 767.98px){.sidebar-menu-container{padding-bottom:5em}}.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 .footnotes ol{margin-left:.5em}#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{--bs-btn-color: #fefefe;--bs-btn-bg: #6c757d;--bs-btn-border-color: #6c757d;--bs-btn-hover-color: #fefefe;--bs-btn-hover-bg: #828a91;--bs-btn-hover-border-color: #7b838a;--bs-btn-focus-shadow-rgb: 130, 138, 144;--bs-btn-active-color: #000;--bs-btn-active-bg: #899197;--bs-btn-active-border-color: #7b838a;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #ffffff;--bs-btn-disabled-bg: #6c757d;--bs-btn-disabled-border-color: #6c757d}nav.quarto-secondary-nav.color-navbar{background-color:#517699;color:#fdfefe}nav.quarto-secondary-nav.color-navbar h1,nav.quarto-secondary-nav.color-navbar .h1,nav.quarto-secondary-nav.color-navbar .quarto-btn-toggle{color:#fdfefe}@media(max-width: 991.98px){body.nav-sidebar .quarto-title-banner{margin-bottom:0;padding-bottom:1em}body.nav-sidebar #title-block-header{margin-block-end:0}}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)}.code-annotated .code-copy-button{margin-right:1.25em;margin-top:0;padding-bottom:0;padding-top:3px}.code-annotation-gutter-bg{background-color:#fff}.code-annotation-gutter{background-color:rgba(233,236,239,.65)}.code-annotation-gutter,.code-annotation-gutter-bg{height:100%;width:calc(20px + .5em);position:absolute;top:0;right:0}dl.code-annotation-container-grid dt{margin-right:1em;margin-top:.25rem}dl.code-annotation-container-grid dt{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;color:#383f45;border:solid #383f45 1px;border-radius:50%;height:22px;width:22px;line-height:22px;font-size:11px;text-align:center;vertical-align:middle;text-decoration:none}dl.code-annotation-container-grid dt[data-target-cell]{cursor:pointer}dl.code-annotation-container-grid dt[data-target-cell].code-annotation-active{color:#fff;border:solid #aaa 1px;background-color:#aaa}pre.code-annotation-code{padding-top:0;padding-bottom:0}pre.code-annotation-code code{z-index:3}#code-annotation-line-highlight-gutter{width:100%;border-top:solid rgba(170,170,170,.2666666667) 1px;border-bottom:solid rgba(170,170,170,.2666666667) 1px;z-index:2;background-color:rgba(170,170,170,.1333333333)}#code-annotation-line-highlight{margin-left:-4em;width:calc(100% + 4em);border-top:solid rgba(170,170,170,.2666666667) 1px;border-bottom:solid rgba(170,170,170,.2666666667) 1px;z-index:2;background-color:rgba(170,170,170,.1333333333)}code.sourceCode .code-annotation-anchor.code-annotation-active{background-color:var(--quarto-hl-normal-color, #aaaaaa);border:solid var(--quarto-hl-normal-color, #aaaaaa) 1px;color:#e9ecef;font-weight:bolder}code.sourceCode .code-annotation-anchor{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;color:var(--quarto-hl-co-color);border:solid var(--quarto-hl-co-color) 1px;border-radius:50%;height:18px;width:18px;font-size:9px;margin-top:2px}code.sourceCode button.code-annotation-anchor{padding:2px;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none}code.sourceCode a.code-annotation-anchor{line-height:18px;text-align:center;vertical-align:middle;cursor:default;text-decoration:none}@media print{.page-columns .column-screen-inset{grid-column:page-start-inset/page-end-inset;z-index:998;opacity:.999}.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;opacity:.999}.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;opacity:.999}.page-columns .column-screen-inset-right table{background:#fff}.page-columns .column-screen{grid-column:page-start/page-end;z-index:998;opacity:.999}.page-columns .column-screen table{background:#fff}.page-columns .column-screen-left{grid-column:page-start/body-content-end;z-index:998;opacity:.999}.page-columns .column-screen-left table{background:#fff}.page-columns .column-screen-right{grid-column:body-content-start/page-end;z-index:998;opacity:.999}.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:#f8f9fa;z-index:998;opacity:.999;margin-bottom:1em}}.quarto-video{margin-bottom:1em}.table{border-top:1px solid #d3d8dc;border-bottom:1px solid #d3d8dc}.table>thead{border-top-width:0;border-bottom:1px solid #9ba5ae}.table a{word-break:break-word}.table>:not(caption)>*>*{background-color:unset;color:unset}#quarto-document-content .crosstalk-input .checkbox input[type=checkbox],#quarto-document-content .crosstalk-input .checkbox-inline input[type=checkbox]{position:unset;margin-top:unset;margin-left:unset}#quarto-document-content .row{margin-left:unset;margin-right:unset}.quarto-xref{white-space:nowrap}a.external:after{content:"";background-image:url('data:image/svg+xml,');background-size:contain;background-repeat:no-repeat;background-position:center center;margin-left:.2em;padding-right:.75em}div.sourceCode code a.external:after{content:none}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{background-color:rgba(219,219,219,.8)}.quarto-dark .code-with-filename .code-with-filename-file{background-color:#555}.code-with-filename .code-with-filename-file strong{font-weight:400}.quarto-title-banner{margin-bottom:1em;color:#fdfefe;background:#517699}.quarto-title-banner a{color:#fdfefe}.quarto-title-banner h1,.quarto-title-banner .h1,.quarto-title-banner h2,.quarto-title-banner .h2{color:#fdfefe}.quarto-title-banner .code-tools-button{color:#b9dcdc}.quarto-title-banner .code-tools-button:hover{color:#fdfefe}.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}}@media(max-width: 767.98px){body.hypothesis-enabled #title-block-header>*{padding-right:20px}}main.quarto-banner-title-block>section:first-child>h2,main.quarto-banner-title-block>section:first-child>.h2,main.quarto-banner-title-block>section:first-child>h3,main.quarto-banner-title-block>section:first-child>.h3,main.quarto-banner-title-block>section:first-child>h4,main.quarto-banner-title-block>section:first-child>.h4{margin-top:0}.quarto-title .quarto-categories{display:flex;flex-wrap:wrap;row-gap:.5em;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}.quarto-title-meta-container{display:grid;grid-template-columns:1fr auto}.quarto-title-meta-column-end{display:flex;flex-direction:column;padding-left:1em}.quarto-title-meta-column-end a .bi{margin-right:.3em}#title-block-header.quarto-title-block.default .quarto-title-meta{display:grid;grid-template-columns:minmax(max-content, 1fr) 1fr;grid-column-gap:1em}#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:-0.2em;height:.8em;width:.8em}#title-block-header.quarto-title-block.default .quarto-title-author-email{opacity:.7}#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 p.affiliation:last-of-type{margin-bottom:.1em}#title-block-header.quarto-title-block.default p.affiliation{margin-bottom:.1em}#title-block-header.quarto-title-block.default .keywords,#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 .keywords>p,#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 .keywords>p:last-of-type,#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 .keywords .block-title,#title-block-header.quarto-title-block.default .description .block-title,#title-block-header.quarto-title-block.default .abstract .block-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:minmax(max-content, 1fr) 1fr;grid-column-gap:1em}.quarto-title-tools-only{display:flex;justify-content:right} diff --git a/docs/site_libs/bootstrap/bootstrap.min.js b/docs/site_libs/bootstrap/bootstrap.min.js new file mode 100644 index 0000000..e8f21f7 --- /dev/null +++ b/docs/site_libs/bootstrap/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v5.3.1 (https://getbootstrap.com/) + * Copyright 2011-2023 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=new Map,e={set(e,i,n){t.has(e)||t.set(e,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i="transitionend",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),s=t=>{t.dispatchEvent(new Event(i))},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(n(t)):null,a=t=>{if(!o(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},l=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),c=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?c(t.parentNode):null},h=()=>{},d=t=>{t.offsetHeight},u=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,f=[],p=()=>"rtl"===document.documentElement.dir,m=t=>{var e;e=()=>{const e=u();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?(f.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of f)t()})),f.push(e)):e()},g=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,_=(t,e,n=!0)=>{if(!n)return void g(t);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})(e)+5;let r=!1;const a=({target:n})=>{n===e&&(r=!0,e.removeEventListener(i,a),g(t))};e.addEventListener(i,a),setTimeout((()=>{r||s(e)}),o)},b=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},v=/[^.]*(?=\..*)\.|.*/,y=/\..*/,w=/::\d+$/,A={};let E=1;const T={mouseenter:"mouseover",mouseleave:"mouseout"},C=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 O(t,e){return e&&`${e}::${E++}`||t.uidEvent||E++}function x(t){const e=O(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function k(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function L(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=I(t);return C.has(o)||(o=t),[n,s,o]}function S(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=L(e,i,n);if(e in T){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=x(t),c=l[a]||(l[a]={}),h=k(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=O(r,e.replace(v,"")),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(const a of o)if(a===r)return P(s,{delegateTarget:r}),n.oneOff&&N.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return P(n,{delegateTarget:t}),i.oneOff&&N.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function D(t,e,i,n,s){const o=k(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function $(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&D(t,e,i,r.callable,r.delegationSelector)}function I(t){return t=t.replace(y,""),T[t]||t}const N={on(t,e,i,n){S(t,e,i,n,!1)},one(t,e,i,n){S(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=L(e,i,n),a=r!==e,l=x(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))$(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(w,"");a&&!e.includes(s)||D(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;D(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=u();let s=null,o=!0,r=!0,a=!1;e!==I(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=P(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function P(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function M(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function j(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const F={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${j(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${j(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=M(t.dataset[n])}return e},getDataAttribute:(t,e)=>M(t.getAttribute(`data-bs-${j(e)}`))};class H{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=o(e)?F.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...o(e)?F.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],r=o(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(r))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${r}" but expected type "${s}".`)}var i}}class W extends H{constructor(t,i){super(),(t=r(t))&&(this._element=t,this._config=this._getConfig(i),e.set(this._element,this.constructor.DATA_KEY,this))}dispose(){e.remove(this._element,this.constructor.DATA_KEY),N.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){_(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return e.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.3.1"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const B=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 n(e)},z={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.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);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=>!l(t)&&a(t)))},getSelectorFromElement(t){const e=B(t);return e&&z.findOne(e)?e:null},getElementFromSelector(t){const e=B(t);return e?z.findOne(e):null},getMultipleElementsFromSelector(t){const e=B(t);return e?z.find(e):[]}},R=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;N.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),l(this))return;const s=z.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))},q=".bs.alert",V=`close${q}`,K=`closed${q}`;class Q extends W{static get NAME(){return"alert"}close(){if(N.trigger(this._element,V).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(),N.trigger(this._element,K),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Q.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(Q,"close"),m(Q);const X='[data-bs-toggle="button"]';class Y extends W{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=Y.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}N.on(document,"click.bs.button.data-api",X,(t=>{t.preventDefault();const e=t.target.closest(X);Y.getOrCreateInstance(e).toggle()})),m(Y);const U=".bs.swipe",G=`touchstart${U}`,J=`touchmove${U}`,Z=`touchend${U}`,tt=`pointerdown${U}`,et=`pointerup${U}`,it={endCallback:null,leftCallback:null,rightCallback:null},nt={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class st extends H{constructor(t,e){super(),this._element=t,t&&st.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return it}static get DefaultType(){return nt}static get NAME(){return"swipe"}dispose(){N.off(this._element,U)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),g(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&g(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(N.on(this._element,tt,(t=>this._start(t))),N.on(this._element,et,(t=>this._end(t))),this._element.classList.add("pointer-event")):(N.on(this._element,G,(t=>this._start(t))),N.on(this._element,J,(t=>this._move(t))),N.on(this._element,Z,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const ot=".bs.carousel",rt=".data-api",at="next",lt="prev",ct="left",ht="right",dt=`slide${ot}`,ut=`slid${ot}`,ft=`keydown${ot}`,pt=`mouseenter${ot}`,mt=`mouseleave${ot}`,gt=`dragstart${ot}`,_t=`load${ot}${rt}`,bt=`click${ot}${rt}`,vt="carousel",yt="active",wt=".active",At=".carousel-item",Et=wt+At,Tt={ArrowLeft:ht,ArrowRight:ct},Ct={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},Ot={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class xt extends W{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=z.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===vt&&this.cycle()}static get Default(){return Ct}static get DefaultType(){return Ot}static get NAME(){return"carousel"}next(){this._slide(at)}nextWhenVisible(){!document.hidden&&a(this._element)&&this.next()}prev(){this._slide(lt)}pause(){this._isSliding&&s(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?N.one(this._element,ut,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void N.one(this._element,ut,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?at:lt;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&N.on(this._element,ft,(t=>this._keydown(t))),"hover"===this._config.pause&&(N.on(this._element,pt,(()=>this.pause())),N.on(this._element,mt,(()=>this._maybeEnableCycle()))),this._config.touch&&st.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of z.find(".carousel-item img",this._element))N.on(t,gt,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(ct)),rightCallback:()=>this._slide(this._directionToOrder(ht)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new st(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=Tt[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=z.findOne(wt,this._indicatorsElement);e.classList.remove(yt),e.removeAttribute("aria-current");const i=z.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(yt),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===at,s=e||b(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>N.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(dt).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),d(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(yt),i.classList.remove(yt,c,l),this._isSliding=!1,r(ut)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return z.findOne(Et,this._element)}_getItems(){return z.find(At,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return p()?t===ct?lt:at:t===ct?at:lt}_orderToDirection(t){return p()?t===lt?ct:ht:t===lt?ht:ct}static jQueryInterface(t){return this.each((function(){const e=xt.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}N.on(document,bt,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=z.getElementFromSelector(this);if(!e||!e.classList.contains(vt))return;t.preventDefault();const i=xt.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===F.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),N.on(window,_t,(()=>{const t=z.find('[data-bs-ride="carousel"]');for(const e of t)xt.getOrCreateInstance(e)})),m(xt);const kt=".bs.collapse",Lt=`show${kt}`,St=`shown${kt}`,Dt=`hide${kt}`,$t=`hidden${kt}`,It=`click${kt}.data-api`,Nt="show",Pt="collapse",Mt="collapsing",jt=`:scope .${Pt} .${Pt}`,Ft='[data-bs-toggle="collapse"]',Ht={parent:null,toggle:!0},Wt={parent:"(null|element)",toggle:"boolean"};class Bt extends W{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=z.find(Ft);for(const t of i){const e=z.getSelectorFromElement(t),i=z.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return Ht}static get DefaultType(){return Wt}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>Bt.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(N.trigger(this._element,Lt).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(Pt),this._element.classList.add(Mt),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(Mt),this._element.classList.add(Pt,Nt),this._element.style[e]="",N.trigger(this._element,St)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(N.trigger(this._element,Dt).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,d(this._element),this._element.classList.add(Mt),this._element.classList.remove(Pt,Nt);for(const t of this._triggerArray){const e=z.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(Mt),this._element.classList.add(Pt),N.trigger(this._element,$t)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(Nt)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=r(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(Ft);for(const e of t){const t=z.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=z.find(jt,this._config.parent);return z.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=Bt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}N.on(document,It,Ft,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of z.getMultipleElementsFromSelector(this))Bt.getOrCreateInstance(t,{toggle:!1}).toggle()})),m(Bt);var zt="top",Rt="bottom",qt="right",Vt="left",Kt="auto",Qt=[zt,Rt,qt,Vt],Xt="start",Yt="end",Ut="clippingParents",Gt="viewport",Jt="popper",Zt="reference",te=Qt.reduce((function(t,e){return t.concat([e+"-"+Xt,e+"-"+Yt])}),[]),ee=[].concat(Qt,[Kt]).reduce((function(t,e){return t.concat([e,e+"-"+Xt,e+"-"+Yt])}),[]),ie="beforeRead",ne="read",se="afterRead",oe="beforeMain",re="main",ae="afterMain",le="beforeWrite",ce="write",he="afterWrite",de=[ie,ne,se,oe,re,ae,le,ce,he];function ue(t){return t?(t.nodeName||"").toLowerCase():null}function fe(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function pe(t){return t instanceof fe(t).Element||t instanceof Element}function me(t){return t instanceof fe(t).HTMLElement||t instanceof HTMLElement}function ge(t){return"undefined"!=typeof ShadowRoot&&(t instanceof fe(t).ShadowRoot||t instanceof ShadowRoot)}const _e={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];me(s)&&ue(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}),{});me(n)&&ue(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function be(t){return t.split("-")[0]}var ve=Math.max,ye=Math.min,we=Math.round;function Ae(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function Ee(){return!/^((?!chrome|android).)*safari/i.test(Ae())}function Te(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&me(t)&&(s=t.offsetWidth>0&&we(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&we(n.height)/t.offsetHeight||1);var r=(pe(t)?fe(t):window).visualViewport,a=!Ee()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function Ce(t){var e=Te(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 Oe(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&ge(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function xe(t){return fe(t).getComputedStyle(t)}function ke(t){return["table","td","th"].indexOf(ue(t))>=0}function Le(t){return((pe(t)?t.ownerDocument:t.document)||window.document).documentElement}function Se(t){return"html"===ue(t)?t:t.assignedSlot||t.parentNode||(ge(t)?t.host:null)||Le(t)}function De(t){return me(t)&&"fixed"!==xe(t).position?t.offsetParent:null}function $e(t){for(var e=fe(t),i=De(t);i&&ke(i)&&"static"===xe(i).position;)i=De(i);return i&&("html"===ue(i)||"body"===ue(i)&&"static"===xe(i).position)?e:i||function(t){var e=/firefox/i.test(Ae());if(/Trident/i.test(Ae())&&me(t)&&"fixed"===xe(t).position)return null;var i=Se(t);for(ge(i)&&(i=i.host);me(i)&&["html","body"].indexOf(ue(i))<0;){var n=xe(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 Ie(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function Ne(t,e,i){return ve(t,ye(e,i))}function Pe(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function Me(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const je={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=be(i.placement),l=Ie(a),c=[Vt,qt].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return Pe("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:Me(t,Qt))}(s.padding,i),d=Ce(o),u="y"===l?zt:Vt,f="y"===l?Rt:qt,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=$e(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,A=Ne(v,w,y),E=l;i.modifiersData[n]=((e={})[E]=A,e.centerOffset=A-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)))&&Oe(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Fe(t){return t.split("-")[1]}var He={top:"auto",right:"auto",bottom:"auto",left:"auto"};function We(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=t.isFixed,u=r.x,f=void 0===u?0:u,p=r.y,m=void 0===p?0:p,g="function"==typeof h?h({x:f,y:m}):{x:f,y:m};f=g.x,m=g.y;var _=r.hasOwnProperty("x"),b=r.hasOwnProperty("y"),v=Vt,y=zt,w=window;if(c){var A=$e(i),E="clientHeight",T="clientWidth";A===fe(i)&&"static"!==xe(A=Le(i)).position&&"absolute"===a&&(E="scrollHeight",T="scrollWidth"),(s===zt||(s===Vt||s===qt)&&o===Yt)&&(y=Rt,m-=(d&&A===w&&w.visualViewport?w.visualViewport.height:A[E])-n.height,m*=l?1:-1),s!==Vt&&(s!==zt&&s!==Rt||o!==Yt)||(v=qt,f-=(d&&A===w&&w.visualViewport?w.visualViewport.width:A[T])-n.width,f*=l?1:-1)}var C,O=Object.assign({position:a},c&&He),x=!0===h?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:we(i*s)/s||0,y:we(n*s)/s||0}}({x:f,y:m},fe(i)):{x:f,y:m};return f=x.x,m=x.y,l?Object.assign({},O,((C={})[y]=b?"0":"",C[v]=_?"0":"",C.transform=(w.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",C)):Object.assign({},O,((e={})[y]=b?m+"px":"",e[v]=_?f+"px":"",e.transform="",e))}const Be={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:be(e.placement),variation:Fe(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,We(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,We(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 ze={passive:!0};const Re={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=fe(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,ze)})),a&&l.addEventListener("resize",i.update,ze),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,ze)})),a&&l.removeEventListener("resize",i.update,ze)}},data:{}};var qe={left:"right",right:"left",bottom:"top",top:"bottom"};function Ve(t){return t.replace(/left|right|bottom|top/g,(function(t){return qe[t]}))}var Ke={start:"end",end:"start"};function Qe(t){return t.replace(/start|end/g,(function(t){return Ke[t]}))}function Xe(t){var e=fe(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function Ye(t){return Te(Le(t)).left+Xe(t).scrollLeft}function Ue(t){var e=xe(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ge(t){return["html","body","#document"].indexOf(ue(t))>=0?t.ownerDocument.body:me(t)&&Ue(t)?t:Ge(Se(t))}function Je(t,e){var i;void 0===e&&(e=[]);var n=Ge(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=fe(n),r=s?[o].concat(o.visualViewport||[],Ue(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(Je(Se(r)))}function Ze(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function ti(t,e,i){return e===Gt?Ze(function(t,e){var i=fe(t),n=Le(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=Ee();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+Ye(t),y:l}}(t,i)):pe(e)?function(t,e){var i=Te(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):Ze(function(t){var e,i=Le(t),n=Xe(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=ve(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=ve(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+Ye(t),l=-n.scrollTop;return"rtl"===xe(s||i).direction&&(a+=ve(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(Le(t)))}function ei(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?be(s):null,r=s?Fe(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case zt:e={x:a,y:i.y-n.height};break;case Rt:e={x:a,y:i.y+i.height};break;case qt:e={x:i.x+i.width,y:l};break;case Vt:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?Ie(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case Xt:e[c]=e[c]-(i[h]/2-n[h]/2);break;case Yt:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function ii(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.strategy,r=void 0===o?t.strategy:o,a=i.boundary,l=void 0===a?Ut:a,c=i.rootBoundary,h=void 0===c?Gt:c,d=i.elementContext,u=void 0===d?Jt:d,f=i.altBoundary,p=void 0!==f&&f,m=i.padding,g=void 0===m?0:m,_=Pe("number"!=typeof g?g:Me(g,Qt)),b=u===Jt?Zt:Jt,v=t.rects.popper,y=t.elements[p?b:u],w=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=Je(Se(t)),i=["absolute","fixed"].indexOf(xe(t).position)>=0&&me(t)?$e(t):t;return pe(i)?e.filter((function(t){return pe(t)&&Oe(t,i)&&"body"!==ue(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=ti(t,i,n);return e.top=ve(s.top,e.top),e.right=ye(s.right,e.right),e.bottom=ye(s.bottom,e.bottom),e.left=ve(s.left,e.left),e}),ti(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(pe(y)?y:y.contextElement||Le(t.elements.popper),l,h,r),A=Te(t.elements.reference),E=ei({reference:A,element:v,strategy:"absolute",placement:s}),T=Ze(Object.assign({},v,E)),C=u===Jt?T:A,O={top:w.top-C.top+_.top,bottom:C.bottom-w.bottom+_.bottom,left:w.left-C.left+_.left,right:C.right-w.right+_.right},x=t.modifiersData.offset;if(u===Jt&&x){var k=x[s];Object.keys(O).forEach((function(t){var e=[qt,Rt].indexOf(t)>=0?1:-1,i=[zt,Rt].indexOf(t)>=0?"y":"x";O[t]+=k[i]*e}))}return O}function ni(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?ee:l,h=Fe(n),d=h?a?te:te.filter((function(t){return Fe(t)===h})):Qt,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]=ii(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[be(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const si={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,_=be(g),b=l||(_!==g&&p?function(t){if(be(t)===Kt)return[];var e=Ve(t);return[Qe(t),e,Qe(e)]}(g):[Ve(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(be(i)===Kt?ni(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,A=new Map,E=!0,T=v[0],C=0;C=0,S=L?"width":"height",D=ii(e,{placement:O,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),$=L?k?qt:Vt:k?Rt:zt;y[S]>w[S]&&($=Ve($));var I=Ve($),N=[];if(o&&N.push(D[x]<=0),a&&N.push(D[$]<=0,D[I]<=0),N.every((function(t){return t}))){T=O,E=!1;break}A.set(O,N)}if(E)for(var P=function(t){var e=v.find((function(e){var i=A.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"!==P(M);M--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function oi(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 ri(t){return[zt,qt,Rt,Vt].some((function(e){return t[e]>=0}))}const ai={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=ii(e,{elementContext:"reference"}),a=ii(e,{altBoundary:!0}),l=oi(r,n),c=oi(a,s,o),h=ri(l),d=ri(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})}},li={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=ee.reduce((function(t,i){return t[i]=function(t,e,i){var n=be(t),s=[Vt,zt].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,[Vt,qt].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}},ci={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=ei({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},hi={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=ii(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=be(e.placement),b=Fe(e.placement),v=!b,y=Ie(_),w="x"===y?"y":"x",A=e.modifiersData.popperOffsets,E=e.rects.reference,T=e.rects.popper,C="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,O="number"==typeof C?{mainAxis:C,altAxis:C}:Object.assign({mainAxis:0,altAxis:0},C),x=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,k={x:0,y:0};if(A){if(o){var L,S="y"===y?zt:Vt,D="y"===y?Rt:qt,$="y"===y?"height":"width",I=A[y],N=I+g[S],P=I-g[D],M=f?-T[$]/2:0,j=b===Xt?E[$]:T[$],F=b===Xt?-T[$]:-E[$],H=e.elements.arrow,W=f&&H?Ce(H):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},z=B[S],R=B[D],q=Ne(0,E[$],W[$]),V=v?E[$]/2-M-q-z-O.mainAxis:j-q-z-O.mainAxis,K=v?-E[$]/2+M+q+R+O.mainAxis:F+q+R+O.mainAxis,Q=e.elements.arrow&&$e(e.elements.arrow),X=Q?"y"===y?Q.clientTop||0:Q.clientLeft||0:0,Y=null!=(L=null==x?void 0:x[y])?L:0,U=I+K-Y,G=Ne(f?ye(N,I+V-Y-X):N,I,f?ve(P,U):P);A[y]=G,k[y]=G-I}if(a){var J,Z="x"===y?zt:Vt,tt="x"===y?Rt:qt,et=A[w],it="y"===w?"height":"width",nt=et+g[Z],st=et-g[tt],ot=-1!==[zt,Vt].indexOf(_),rt=null!=(J=null==x?void 0:x[w])?J:0,at=ot?nt:et-E[it]-T[it]-rt+O.altAxis,lt=ot?et+E[it]+T[it]-rt-O.altAxis:st,ct=f&&ot?function(t,e,i){var n=Ne(t,e,i);return n>i?i:n}(at,et,lt):Ne(f?at:nt,et,f?lt:st);A[w]=ct,k[w]=ct-et}e.modifiersData[n]=k}},requiresIfExists:["offset"]};function di(t,e,i){void 0===i&&(i=!1);var n,s,o=me(e),r=me(e)&&function(t){var e=t.getBoundingClientRect(),i=we(e.width)/t.offsetWidth||1,n=we(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=Le(e),l=Te(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==ue(e)||Ue(a))&&(c=(n=e)!==fe(n)&&me(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:Xe(n)),me(e)?((h=Te(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=Ye(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function ui(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 fi={placement:"bottom",modifiers:[],strategy:"absolute"};function pi(){for(var t=arguments.length,e=new Array(t),i=0;iNumber.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(this._inNavbar||"static"===this._config.display)&&(F.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...g(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=z.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>a(t)));i.length&&b(i,e,t===Ti,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=qi.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(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=z.find(Ni);for(const i of e){const e=qi.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Ei,Ti].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Ii)?this:z.prev(this,Ii)[0]||z.next(this,Ii)[0]||z.findOne(Ii,t.delegateTarget.parentNode),o=qi.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}N.on(document,Si,Ii,qi.dataApiKeydownHandler),N.on(document,Si,Pi,qi.dataApiKeydownHandler),N.on(document,Li,qi.clearMenus),N.on(document,Di,qi.clearMenus),N.on(document,Li,Ii,(function(t){t.preventDefault(),qi.getOrCreateInstance(this).toggle()})),m(qi);const Vi="backdrop",Ki="show",Qi=`mousedown.bs.${Vi}`,Xi={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Yi={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Ui extends H{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Xi}static get DefaultType(){return Yi}static get NAME(){return Vi}show(t){if(!this._config.isVisible)return void g(t);this._append();const e=this._getElement();this._config.isAnimated&&d(e),e.classList.add(Ki),this._emulateAnimation((()=>{g(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Ki),this._emulateAnimation((()=>{this.dispose(),g(t)}))):g(t)}dispose(){this._isAppended&&(N.off(this._element,Qi),this._element.remove(),this._isAppended=!1)}_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}_configAfterMerge(t){return t.rootElement=r(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),N.on(t,Qi,(()=>{g(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){_(t,this._getElement(),this._config.isAnimated)}}const Gi=".bs.focustrap",Ji=`focusin${Gi}`,Zi=`keydown.tab${Gi}`,tn="backward",en={autofocus:!0,trapElement:null},nn={autofocus:"boolean",trapElement:"element"};class sn extends H{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return en}static get DefaultType(){return nn}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),N.off(document,Gi),N.on(document,Ji,(t=>this._handleFocusin(t))),N.on(document,Zi,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,N.off(document,Gi))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=z.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===tn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?tn:"forward")}}const on=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",rn=".sticky-top",an="padding-right",ln="margin-right";class cn{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,an,(e=>e+t)),this._setElementAttributes(on,an,(e=>e+t)),this._setElementAttributes(rn,ln,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,an),this._resetElementAttributes(on,an),this._resetElementAttributes(rn,ln)}isOverflowing(){return this.getWidth()>0}_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).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&F.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=F.getDataAttribute(t,e);null!==i?(F.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(o(t))e(t);else for(const i of z.find(t,this._element))e(i)}}const hn=".bs.modal",dn=`hide${hn}`,un=`hidePrevented${hn}`,fn=`hidden${hn}`,pn=`show${hn}`,mn=`shown${hn}`,gn=`resize${hn}`,_n=`click.dismiss${hn}`,bn=`mousedown.dismiss${hn}`,vn=`keydown.dismiss${hn}`,yn=`click${hn}.data-api`,wn="modal-open",An="show",En="modal-static",Tn={backdrop:!0,focus:!0,keyboard:!0},Cn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class On extends W{constructor(t,e){super(t,e),this._dialog=z.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new cn,this._addEventListeners()}static get Default(){return Tn}static get DefaultType(){return Cn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||N.trigger(this._element,pn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(wn),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(N.trigger(this._element,dn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(An),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){N.off(window,hn),N.off(this._dialog,hn),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Ui({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||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;const e=z.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),d(this._element),this._element.classList.add(An),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,N.trigger(this._element,mn,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){N.on(this._element,vn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),N.on(window,gn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),N.on(this._element,bn,(t=>{N.one(this._element,_n,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_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(wn),this._resetAdjustments(),this._scrollBar.reset(),N.trigger(this._element,fn)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(N.trigger(this._element,un).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(En)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(En),this._queueCallback((()=>{this._element.classList.remove(En),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=p()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=p()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=On.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}N.on(document,yn,'[data-bs-toggle="modal"]',(function(t){const e=z.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),N.one(e,pn,(t=>{t.defaultPrevented||N.one(e,fn,(()=>{a(this)&&this.focus()}))}));const i=z.findOne(".modal.show");i&&On.getInstance(i).hide(),On.getOrCreateInstance(e).toggle(this)})),R(On),m(On);const xn=".bs.offcanvas",kn=".data-api",Ln=`load${xn}${kn}`,Sn="show",Dn="showing",$n="hiding",In=".offcanvas.show",Nn=`show${xn}`,Pn=`shown${xn}`,Mn=`hide${xn}`,jn=`hidePrevented${xn}`,Fn=`hidden${xn}`,Hn=`resize${xn}`,Wn=`click${xn}${kn}`,Bn=`keydown.dismiss${xn}`,zn={backdrop:!0,keyboard:!0,scroll:!1},Rn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class qn extends W{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return zn}static get DefaultType(){return Rn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||N.trigger(this._element,Nn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new cn).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Dn),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(Sn),this._element.classList.remove(Dn),N.trigger(this._element,Pn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(N.trigger(this._element,Mn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add($n),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(Sn,$n),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new cn).reset(),N.trigger(this._element,Fn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Ui({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():N.trigger(this._element,jn)}:null})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_addEventListeners(){N.on(this._element,Bn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():N.trigger(this._element,jn))}))}static jQueryInterface(t){return this.each((function(){const e=qn.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)}}))}}N.on(document,Wn,'[data-bs-toggle="offcanvas"]',(function(t){const e=z.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this))return;N.one(e,Fn,(()=>{a(this)&&this.focus()}));const i=z.findOne(In);i&&i!==e&&qn.getInstance(i).hide(),qn.getOrCreateInstance(e).toggle(this)})),N.on(window,Ln,(()=>{for(const t of z.find(In))qn.getOrCreateInstance(t).show()})),N.on(window,Hn,(()=>{for(const t of z.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&qn.getOrCreateInstance(t).hide()})),R(qn),m(qn);const Vn={"*":["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:[]},Kn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Qn=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Xn=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Kn.has(i)||Boolean(Qn.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Yn={allowList:Vn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Un={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Gn={entry:"(string|element|function|null)",selector:"(string|element)"};class Jn extends H{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Yn}static get DefaultType(){return Un}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},Gn)}_setContent(t,e,i){const n=z.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?o(e)?this._putElementInTemplate(r(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Xn(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return g(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const Zn=new Set(["sanitize","allowList","sanitizeFn"]),ts="fade",es="show",is=".modal",ns="hide.bs.modal",ss="hover",os="focus",rs={AUTO:"auto",TOP:"top",RIGHT:p()?"left":"right",BOTTOM:"bottom",LEFT:p()?"right":"left"},as={allowList:Vn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},ls={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class cs extends W{constructor(t,e){if(void 0===vi)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,e),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return as}static get DefaultType(){return ls}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),N.off(this._element.closest(is),ns,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),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=N.trigger(this._element,this.constructor.eventName("show")),e=(c(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),N.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.on(t,"mouseover",h);this._queueCallback((()=>{N.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!N.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.off(t,"mouseover",h);this._activeTrigger.click=!1,this._activeTrigger[os]=!1,this._activeTrigger[ss]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),N.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(ts,es),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(ts),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new Jn({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{".tooltip-inner":this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(ts)}_isShown(){return this.tip&&this.tip.classList.contains(es)}_createPopper(t){const e=g(this._config.placement,[this,t,this._element]),i=rs[e.toUpperCase()];return bi(this._element,t,this._getPopperConfig(i))}_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 g(t,[this._element])}_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:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...g(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)N.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===ss?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===ss?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");N.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?os:ss]=!0,e._enter()})),N.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?os:ss]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},N.on(this._element.closest(is),ns,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=F.getDataAttributes(this._element);for(const t of Object.keys(e))Zn.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return 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()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=cs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(cs);const hs={...cs.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},ds={...cs.DefaultType,content:"(null|string|element|function)"};class us extends cs{static get Default(){return hs}static get DefaultType(){return ds}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{".popover-header":this._getTitle(),".popover-body":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=us.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(us);const fs=".bs.scrollspy",ps=`activate${fs}`,ms=`click${fs}`,gs=`load${fs}.data-api`,_s="active",bs="[href]",vs=".nav-link",ys=`${vs}, .nav-item > ${vs}, .list-group-item`,ws={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},As={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class Es extends W{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return ws}static get DefaultType(){return As}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=r(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(N.off(this._config.target,ms),N.on(this._config.target,ms,bs,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=z.find(bs,this._config.target);for(const e of t){if(!e.hash||l(e))continue;const t=z.findOne(decodeURI(e.hash),this._element);a(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(_s),this._activateParents(t),N.trigger(this._element,ps,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))z.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(_s);else for(const e of z.parents(t,".nav, .list-group"))for(const t of z.prev(e,ys))t.classList.add(_s)}_clearActiveClass(t){t.classList.remove(_s);const e=z.find(`${bs}.${_s}`,t);for(const t of e)t.classList.remove(_s)}static jQueryInterface(t){return this.each((function(){const e=Es.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]()}}))}}N.on(window,gs,(()=>{for(const t of z.find('[data-bs-spy="scroll"]'))Es.getOrCreateInstance(t)})),m(Es);const Ts=".bs.tab",Cs=`hide${Ts}`,Os=`hidden${Ts}`,xs=`show${Ts}`,ks=`shown${Ts}`,Ls=`click${Ts}`,Ss=`keydown${Ts}`,Ds=`load${Ts}`,$s="ArrowLeft",Is="ArrowRight",Ns="ArrowUp",Ps="ArrowDown",Ms="Home",js="End",Fs="active",Hs="fade",Ws="show",Bs=":not(.dropdown-toggle)",zs='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',Rs=`.nav-link${Bs}, .list-group-item${Bs}, [role="tab"]${Bs}, ${zs}`,qs=`.${Fs}[data-bs-toggle="tab"], .${Fs}[data-bs-toggle="pill"], .${Fs}[data-bs-toggle="list"]`;class Vs extends W{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),N.on(this._element,Ss,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?N.trigger(e,Cs,{relatedTarget:t}):null;N.trigger(t,xs,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(Fs),this._activate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),N.trigger(t,ks,{relatedTarget:e})):t.classList.add(Ws)}),t,t.classList.contains(Hs)))}_deactivate(t,e){t&&(t.classList.remove(Fs),t.blur(),this._deactivate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),N.trigger(t,Os,{relatedTarget:e})):t.classList.remove(Ws)}),t,t.classList.contains(Hs)))}_keydown(t){if(![$s,Is,Ns,Ps,Ms,js].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!l(t)));let i;if([Ms,js].includes(t.key))i=e[t.key===Ms?0:e.length-1];else{const n=[Is,Ps].includes(t.key);i=b(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Vs.getOrCreateInstance(i).show())}_getChildren(){return z.find(Rs,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=z.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=z.findOne(t,i);s&&s.classList.toggle(n,e)};n(".dropdown-toggle",Fs),n(".dropdown-menu",Ws),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(Fs)}_getInnerElement(t){return t.matches(Rs)?t:z.findOne(Rs,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=Vs.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}N.on(document,Ls,zs,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this)||Vs.getOrCreateInstance(this).show()})),N.on(window,Ds,(()=>{for(const t of z.find(qs))Vs.getOrCreateInstance(t)})),m(Vs);const Ks=".bs.toast",Qs=`mouseover${Ks}`,Xs=`mouseout${Ks}`,Ys=`focusin${Ks}`,Us=`focusout${Ks}`,Gs=`hide${Ks}`,Js=`hidden${Ks}`,Zs=`show${Ks}`,to=`shown${Ks}`,eo="hide",io="show",no="showing",so={animation:"boolean",autohide:"boolean",delay:"number"},oo={animation:!0,autohide:!0,delay:5e3};class ro extends W{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return oo}static get DefaultType(){return so}static get NAME(){return"toast"}show(){N.trigger(this._element,Zs).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(eo),d(this._element),this._element.classList.add(io,no),this._queueCallback((()=>{this._element.classList.remove(no),N.trigger(this._element,to),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(N.trigger(this._element,Gs).defaultPrevented||(this._element.classList.add(no),this._queueCallback((()=>{this._element.classList.add(eo),this._element.classList.remove(no,io),N.trigger(this._element,Js)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(io),super.dispose()}isShown(){return this._element.classList.contains(io)}_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(){N.on(this._element,Qs,(t=>this._onInteraction(t,!0))),N.on(this._element,Xs,(t=>this._onInteraction(t,!1))),N.on(this._element,Ys,(t=>this._onInteraction(t,!0))),N.on(this._element,Us,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=ro.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return R(ro),m(ro),{Alert:Q,Button:Y,Carousel:xt,Collapse:Bt,Dropdown:qi,Modal:On,Offcanvas:qn,Popover:us,ScrollSpy:Es,Tab:Vs,Toast:ro,Tooltip:cs}})); +//# sourceMappingURL=bootstrap.bundle.min.js.map \ No newline at end of file diff --git a/docs/site_libs/clipboard/clipboard.min.js b/docs/site_libs/clipboard/clipboard.min.js new file mode 100644 index 0000000..1103f81 --- /dev/null +++ b/docs/site_libs/clipboard/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.11 + * 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 b}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),r=n.n(e);function c(t){try{return document.execCommand(t)}catch(t){return}}var a=function(t){t=r()(t);return c("cut"),t};function o(t,e){var n,o,t=(n=t,o="rtl"===document.documentElement.getAttribute("dir"),(t=document.createElement("textarea")).style.fontSize="12pt",t.style.border="0",t.style.padding="0",t.style.margin="0",t.style.position="absolute",t.style[o?"right":"left"]="-9999px",o=window.pageYOffset||document.documentElement.scrollTop,t.style.top="".concat(o,"px"),t.setAttribute("readonly",""),t.value=n,t);return e.container.appendChild(t),e=r()(t),c("copy"),t.remove(),e}var f=function(t){var e=1.anchorjs-link,.anchorjs-link:focus{opacity:1}",A.sheet.cssRules.length),A.sheet.insertRule("[data-anchorjs-icon]::after{content:attr(data-anchorjs-icon)}",A.sheet.cssRules.length),A.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")}',A.sheet.cssRules.length)),h=document.querySelectorAll("[id]"),t=[].map.call(h,function(A){return A.id}),i=0;i\]./()*\\\n\t\b\v\u00A0]/g,"-").replace(/-{2,}/g,"-").substring(0,this.options.truncate).replace(/^-+|-+$/gm,"").toLowerCase()},this.hasAnchorJSLink=function(A){var e=A.firstChild&&-1<(" "+A.firstChild.className+" ").indexOf(" anchorjs-link "),A=A.lastChild&&-1<(" "+A.lastChild.className+" ").indexOf(" anchorjs-link ");return e||A||!1}}}); +// @license-end \ No newline at end of file diff --git a/docs/site_libs/quarto-html/popper.min.js b/docs/site_libs/quarto-html/popper.min.js new file mode 100644 index 0000000..e3726d7 --- /dev/null +++ b/docs/site_libs/quarto-html/popper.min.js @@ -0,0 +1,6 @@ +/** + * @popperjs/core v2.11.7 - 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(){var e=navigator.userAgentData;return null!=e&&e.brands&&Array.isArray(e.brands)?e.brands.map((function(e){return e.brand+"/"+e.version})).join(" "):navigator.userAgent}function c(){return!/^((?!chrome|android).)*safari/i.test(f())}function p(e,o,i){void 0===o&&(o=!1),void 0===i&&(i=!1);var a=e.getBoundingClientRect(),f=1,p=1;o&&r(e)&&(f=e.offsetWidth>0&&s(a.width)/e.offsetWidth||1,p=e.offsetHeight>0&&s(a.height)/e.offsetHeight||1);var u=(n(e)?t(e):window).visualViewport,l=!c()&&i,d=(a.left+(l&&u?u.offsetLeft:0))/f,h=(a.top+(l&&u?u.offsetTop:0))/p,m=a.width/f,v=a.height/p;return{width:m,height:v,top:h,right:d+m,bottom:h+v,left:d,x:d,y:h}}function u(e){var n=t(e);return{scrollLeft:n.pageXOffset,scrollTop:n.pageYOffset}}function l(e){return e?(e.nodeName||"").toLowerCase():null}function d(e){return((n(e)?e.ownerDocument:e.document)||window.document).documentElement}function h(e){return p(d(e)).left+u(e).scrollLeft}function m(e){return t(e).getComputedStyle(e)}function v(e){var t=m(e),n=t.overflow,r=t.overflowX,o=t.overflowY;return/auto|scroll|overlay|hidden/.test(n+o+r)}function y(e,n,o){void 0===o&&(o=!1);var i,a,f=r(n),c=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),m=d(n),y=p(e,c,o),g={scrollLeft:0,scrollTop:0},b={x:0,y:0};return(f||!f&&!o)&&(("body"!==l(n)||v(m))&&(g=(i=n)!==t(i)&&r(i)?{scrollLeft:(a=i).scrollLeft,scrollTop:a.scrollTop}:u(i)),r(n)?((b=p(n,!0)).x+=n.clientLeft,b.y+=n.clientTop):m&&(b.x=h(m))),{x:y.left+g.scrollLeft-b.x,y:y.top+g.scrollTop-b.y,width:y.width,height:y.height}}function g(e){var t=p(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 b(e){return"html"===l(e)?e:e.assignedSlot||e.parentNode||(o(e)?e.host:null)||d(e)}function x(e){return["html","body","#document"].indexOf(l(e))>=0?e.ownerDocument.body:r(e)&&v(e)?e:x(b(e))}function w(e,n){var r;void 0===n&&(n=[]);var o=x(e),i=o===(null==(r=e.ownerDocument)?void 0:r.body),a=t(o),s=i?[a].concat(a.visualViewport||[],v(o)?o:[]):o,f=n.concat(s);return i?f:f.concat(w(b(s)))}function O(e){return["table","td","th"].indexOf(l(e))>=0}function j(e){return r(e)&&"fixed"!==m(e).position?e.offsetParent:null}function E(e){for(var n=t(e),i=j(e);i&&O(i)&&"static"===m(i).position;)i=j(i);return i&&("html"===l(i)||"body"===l(i)&&"static"===m(i).position)?n:i||function(e){var t=/firefox/i.test(f());if(/Trident/i.test(f())&&r(e)&&"fixed"===m(e).position)return null;var n=b(e);for(o(n)&&(n=n.host);r(n)&&["html","body"].indexOf(l(n))<0;){var i=m(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 D="top",A="bottom",L="right",P="left",M="auto",k=[D,A,L,P],W="start",B="end",H="viewport",T="popper",R=k.reduce((function(e,t){return e.concat([t+"-"+W,t+"-"+B])}),[]),S=[].concat(k,[M]).reduce((function(e,t){return e.concat([t,t+"-"+W,t+"-"+B])}),[]),V=["beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite"];function q(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 N(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 I(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function _(e,r,o){return r===H?I(function(e,n){var r=t(e),o=d(e),i=r.visualViewport,a=o.clientWidth,s=o.clientHeight,f=0,p=0;if(i){a=i.width,s=i.height;var u=c();(u||!u&&"fixed"===n)&&(f=i.offsetLeft,p=i.offsetTop)}return{width:a,height:s,x:f+h(e),y:p}}(e,o)):n(r)?function(e,t){var n=p(e,!1,"fixed"===t);return n.top=n.top+e.clientTop,n.left=n.left+e.clientLeft,n.bottom=n.top+e.clientHeight,n.right=n.left+e.clientWidth,n.width=e.clientWidth,n.height=e.clientHeight,n.x=n.left,n.y=n.top,n}(r,o):I(function(e){var t,n=d(e),r=u(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+h(e),c=-r.scrollTop;return"rtl"===m(o||n).direction&&(f+=i(n.clientWidth,o?o.clientWidth:0)-a),{width:a,height:s,x:f,y:c}}(d(e)))}function F(e,t,o,s){var f="clippingParents"===t?function(e){var t=w(b(e)),o=["absolute","fixed"].indexOf(m(e).position)>=0&&r(e)?E(e):e;return n(o)?t.filter((function(e){return n(e)&&N(e,o)&&"body"!==l(e)})):[]}(e):[].concat(t),c=[].concat(f,[o]),p=c[0],u=c.reduce((function(t,n){var r=_(e,n,s);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}),_(e,p,s));return u.width=u.right-u.left,u.height=u.bottom-u.top,u.x=u.left,u.y=u.top,u}function U(e){return e.split("-")[1]}function z(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}function X(e){var t,n=e.reference,r=e.element,o=e.placement,i=o?C(o):null,a=o?U(o):null,s=n.x+n.width/2-r.width/2,f=n.y+n.height/2-r.height/2;switch(i){case D:t={x:s,y:n.y-r.height};break;case A:t={x:s,y:n.y+n.height};break;case L:t={x:n.x+n.width,y:f};break;case P:t={x:n.x-r.width,y:f};break;default:t={x:n.x,y:n.y}}var c=i?z(i):null;if(null!=c){var p="y"===c?"height":"width";switch(a){case W:t[c]=t[c]-(n[p]/2-r[p]/2);break;case B:t[c]=t[c]+(n[p]/2-r[p]/2)}}return t}function Y(e){return Object.assign({},{top:0,right:0,bottom:0,left:0},e)}function G(e,t){return t.reduce((function(t,n){return t[n]=e,t}),{})}function J(e,t){void 0===t&&(t={});var r=t,o=r.placement,i=void 0===o?e.placement:o,a=r.strategy,s=void 0===a?e.strategy:a,f=r.boundary,c=void 0===f?"clippingParents":f,u=r.rootBoundary,l=void 0===u?H:u,h=r.elementContext,m=void 0===h?T:h,v=r.altBoundary,y=void 0!==v&&v,g=r.padding,b=void 0===g?0:g,x=Y("number"!=typeof b?b:G(b,k)),w=m===T?"reference":T,O=e.rects.popper,j=e.elements[y?w:m],E=F(n(j)?j:j.contextElement||d(e.elements.popper),c,l,s),P=p(e.elements.reference),M=X({reference:P,element:O,strategy:"absolute",placement:i}),W=I(Object.assign({},O,M)),B=m===T?W:P,R={top:E.top-B.top+x.top,bottom:B.bottom-E.bottom+x.bottom,left:E.left-B.left+x.left,right:B.right-E.right+x.right},S=e.modifiersData.offset;if(m===T&&S){var V=S[i];Object.keys(R).forEach((function(e){var t=[L,A].indexOf(e)>=0?1:-1,n=[D,A].indexOf(e)>=0?"y":"x";R[e]+=V[n]*t}))}return R}var K={placement:"bottom",modifiers:[],strategy:"absolute"};function Q(){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,[P,L].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}},se={left:"right",right:"left",bottom:"top",top:"bottom"};function fe(e){return e.replace(/left|right|bottom|top/g,(function(e){return se[e]}))}var ce={start:"end",end:"start"};function pe(e){return e.replace(/start|end/g,(function(e){return ce[e]}))}function ue(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?S:f,p=U(r),u=p?s?R:R.filter((function(e){return U(e)===p})):k,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]=J(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 le={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,y=C(v),g=f||(y===v||!h?[fe(v)]:function(e){if(C(e)===M)return[];var t=fe(e);return[pe(e),t,pe(t)]}(v)),b=[v].concat(g).reduce((function(e,n){return e.concat(C(n)===M?ue(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,j=!0,E=b[0],k=0;k=0,S=R?"width":"height",V=J(t,{placement:B,boundary:p,rootBoundary:u,altBoundary:l,padding:c}),q=R?T?L:P:T?A:D;x[S]>w[S]&&(q=fe(q));var N=fe(q),I=[];if(i&&I.push(V[H]<=0),s&&I.push(V[q]<=0,V[N]<=0),I.every((function(e){return e}))){E=B,j=!1;break}O.set(B,I)}if(j)for(var _=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 E=t,"break"},F=h?3:1;F>0;F--){if("break"===_(F))break}t.placement!==E&&(t.modifiersData[r]._skip=!0,t.placement=E,t.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function de(e,t,n){return i(e,a(t,n))}var he={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,v=n.tetherOffset,y=void 0===v?0:v,b=J(t,{boundary:p,rootBoundary:u,padding:d,altBoundary:l}),x=C(t.placement),w=U(t.placement),O=!w,j=z(x),M="x"===j?"y":"x",k=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,V={x:0,y:0};if(k){if(s){var q,N="y"===j?D:P,I="y"===j?A:L,_="y"===j?"height":"width",F=k[j],X=F+b[N],Y=F-b[I],G=m?-H[_]/2:0,K=w===W?B[_]:H[_],Q=w===W?-H[_]:-B[_],Z=t.elements.arrow,$=m&&Z?g(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=de(0,B[_],$[_]),oe=O?B[_]/2-G-re-te-R.mainAxis:K-re-te-R.mainAxis,ie=O?-B[_]/2+G+re+ne+R.mainAxis:Q+re+ne+R.mainAxis,ae=t.elements.arrow&&E(t.elements.arrow),se=ae?"y"===j?ae.clientTop||0:ae.clientLeft||0:0,fe=null!=(q=null==S?void 0:S[j])?q:0,ce=F+ie-fe,pe=de(m?a(X,F+oe-fe-se):X,F,m?i(Y,ce):Y);k[j]=pe,V[j]=pe-F}if(c){var ue,le="x"===j?D:P,he="x"===j?A:L,me=k[M],ve="y"===M?"height":"width",ye=me+b[le],ge=me-b[he],be=-1!==[D,P].indexOf(x),xe=null!=(ue=null==S?void 0:S[M])?ue:0,we=be?ye:me-B[ve]-H[ve]-xe+R.altAxis,Oe=be?me+B[ve]+H[ve]-xe-R.altAxis:ge,je=m&&be?function(e,t,n){var r=de(e,t,n);return r>n?n:r}(we,me,Oe):de(m?we:ye,me,m?Oe:ge);k[M]=je,V[M]=je-me}t.modifiersData[r]=V}},requiresIfExists:["offset"]};var me={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=z(s),c=[P,L].indexOf(s)>=0?"height":"width";if(i&&a){var p=function(e,t){return Y("number"!=typeof(e="function"==typeof e?e(Object.assign({},t.rects,{placement:t.placement})):e)?e:G(e,k))}(o.padding,n),u=g(i),l="y"===f?D:P,d="y"===f?A:L,h=n.rects.reference[c]+n.rects.reference[f]-a[f]-n.rects.popper[c],m=a[f]-n.rects.reference[f],v=E(i),y=v?"y"===f?v.clientHeight||0:v.clientWidth||0:0,b=h/2-m/2,x=p[l],w=y-u[c]-p[d],O=y/2-u[c]/2+b,j=de(x,O,w),M=f;n.modifiersData[r]=((t={})[M]=j,t.centerOffset=j-O,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)))&&N(t.elements.popper,r)&&(t.elements.arrow=r)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function ve(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 ye(e){return[D,L,A,P].some((function(t){return e[t]>=0}))}var ge={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=J(t,{elementContext:"reference"}),s=J(t,{altBoundary:!0}),f=ve(a,r),c=ve(s,o,i),p=ye(f),u=ye(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})}},be=Z({defaultModifiers:[ee,te,oe,ie]}),xe=[ee,te,oe,ie,ae,le,he,me,ge],we=Z({defaultModifiers:xe});e.applyStyles=ie,e.arrow=me,e.computeStyles=oe,e.createPopper=we,e.createPopperLite=be,e.defaultModifiers=xe,e.detectOverflow=J,e.eventListeners=ee,e.flip=le,e.hide=ge,e.offset=ae,e.popperGenerator=Z,e.popperOffsets=te,e.preventOverflow=he,Object.defineProperty(e,"__esModule",{value:!0})})); + diff --git a/docs/site_libs/quarto-html/quarto-syntax-highlighting.css b/docs/site_libs/quarto-html/quarto-syntax-highlighting.css new file mode 100644 index 0000000..d9fd98f --- /dev/null +++ b/docs/site_libs/quarto-html/quarto-syntax-highlighting.css @@ -0,0 +1,203 @@ +/* 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; + font-style: inherit; +} + +code span.at { + color: #657422; + font-style: inherit; +} + +code span.ss { + color: #20794D; + font-style: inherit; +} + +code span.an { + color: #5E5E5E; + font-style: inherit; +} + +code span.fu { + color: #4758AB; + font-style: inherit; +} + +code span.st { + color: #20794D; + font-style: inherit; +} + +code span.cf { + color: #003B4F; + font-style: inherit; +} + +code span.op { + color: #5E5E5E; + font-style: inherit; +} + +code span.er { + color: #AD0000; + font-style: inherit; +} + +code span.bn { + color: #AD0000; + font-style: inherit; +} + +code span.al { + color: #AD0000; + font-style: inherit; +} + +code span.va { + color: #111111; + font-style: inherit; +} + +code span.bu { + font-style: inherit; +} + +code span.ex { + font-style: inherit; +} + +code span.pp { + color: #AD0000; + font-style: inherit; +} + +code span.in { + color: #5E5E5E; + font-style: inherit; +} + +code span.vs { + color: #20794D; + font-style: inherit; +} + +code span.wa { + color: #5E5E5E; + font-style: italic; +} + +code span.do { + color: #5E5E5E; + font-style: italic; +} + +code span.im { + color: #00769E; + font-style: inherit; +} + +code span.ch { + color: #20794D; + font-style: inherit; +} + +code span.dt { + color: #AD0000; + font-style: inherit; +} + +code span.fl { + color: #AD0000; + font-style: inherit; +} + +code span.co { + color: #5E5E5E; + font-style: inherit; +} + +code span.cv { + color: #5E5E5E; + font-style: italic; +} + +code span.cn { + color: #8f5902; + font-style: inherit; +} + +code span.sc { + color: #5E5E5E; + font-style: inherit; +} + +code span.dv { + color: #AD0000; + font-style: inherit; +} + +code span.kw { + color: #003B4F; + font-style: inherit; +} + +.prevent-inlining { + content: " { + // Find any conflicting margin elements and add margins to the + // top to prevent overlap + const marginChildren = window.document.querySelectorAll( + ".column-margin.column-container > *, .margin-caption, .aside" + ); + + let lastBottom = 0; + for (const marginChild of marginChildren) { + if (marginChild.offsetParent !== null) { + // clear the top margin so we recompute it + marginChild.style.marginTop = null; + const top = marginChild.getBoundingClientRect().top + window.scrollY; + if (top < lastBottom) { + const marginChildStyle = window.getComputedStyle(marginChild); + const marginBottom = parseFloat(marginChildStyle["marginBottom"]); + const margin = lastBottom - top + marginBottom; + marginChild.style.marginTop = `${margin}px`; + } + const styles = window.getComputedStyle(marginChild); + const marginTop = parseFloat(styles["marginTop"]); + lastBottom = top + marginChild.getBoundingClientRect().height + marginTop; + } + } +}; + +window.document.addEventListener("DOMContentLoaded", function (_event) { + // Recompute the position of margin elements anytime the body size changes + if (window.ResizeObserver) { + const resizeObserver = new window.ResizeObserver( + throttle(() => { + layoutMarginEls(); + if ( + window.document.body.getBoundingClientRect().width < 990 && + isReaderMode() + ) { + quartoToggleReader(); + } + }, 50) + ); + resizeObserver.observe(window.document.body); + } + + const tocEl = window.document.querySelector('nav.toc-active[role="doc-toc"]'); + const sidebarEl = window.document.getElementById("quarto-sidebar"); + const leftTocEl = window.document.getElementById("quarto-sidebar-toc-left"); + const marginSidebarEl = window.document.getElementById( + "quarto-margin-sidebar" + ); + // function to determine whether the element has a previous sibling that is active + const prevSiblingIsActiveLink = (el) => { + 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; + let elRect; + + return (hiddenRegions) => { + if (el === null) { + return; + } + + // Find the last element of the TOC + const lastChildEl = el.lastElementChild; + + if (lastChildEl) { + // Converts the sidebar to a menu + const convertToMenu = () => { + for (const child of el.children) { + child.style.opacity = 0; + child.style.overflow = "hidden"; + } + + nexttick(() => { + 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.textContent || 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"; + const positionToggle = () => { + // position the element (top left of parent, same width as parent) + if (!elRect) { + elRect = el.getBoundingClientRect(); + } + toggleContainer.style.left = `${elRect.left}px`; + toggleContainer.style.top = `${elRect.top}px`; + toggleContainer.style.width = `${elRect.width}px`; + }; + positionToggle(); + + 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; + } + }; + + // 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(() => { + elRect = undefined; + positionToggle(); + }, 50) + ); + + window.addEventListener("quarto-hrChanged", () => { + elRect = undefined; + }); + + // 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 { + // Find the top and bottom o the element that is being managed + const elTop = el.offsetTop; + const elBottom = + elTop + lastChildEl.offsetTop + lastChildEl.offsetHeight; + + 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; + } + } + } + } + }; + }; + + const tabEls = document.querySelectorAll('a[data-bs-toggle="tab"]'); + for (const tabEl of tabEls) { + const id = tabEl.getAttribute("data-bs-target"); + if (id) { + const columnEl = document.querySelector( + `${id} .column-margin, .tabset-margin-content` + ); + if (columnEl) + tabEl.addEventListener("shown.bs.tab", function (event) { + const el = event.srcElement; + if (el) { + const visibleCls = `${el.id}-margin-content`; + // walk up until we find a parent tabset + let panelTabsetEl = el.parentElement; + while (panelTabsetEl) { + if (panelTabsetEl.classList.contains("panel-tabset")) { + break; + } + panelTabsetEl = panelTabsetEl.parentElement; + } + + if (panelTabsetEl) { + const prevSib = panelTabsetEl.previousElementSibling; + if ( + prevSib && + prevSib.classList.contains("tabset-margin-container") + ) { + const childNodes = prevSib.querySelectorAll( + ".tabset-margin-content" + ); + for (const childEl of childNodes) { + if (childEl.classList.contains(visibleCls)) { + childEl.classList.remove("collapse"); + } else { + childEl.classList.add("collapse"); + } + } + } + } + } + + layoutMarginEls(); + }); + } + } + + // 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 boundRect = el.getBoundingClientRect(); + const top = + boundRect.top + + document.documentElement.scrollTop - + kOverlapPaddingSize; + return { + top, + bottom: top + el.scrollHeight + 2 * kOverlapPaddingSize, + }; + }); + } + + let hasObserved = false; + const visibleItemObserver = (els) => { + let visibleElements = [...els]; + const intersectionObserver = new IntersectionObserver( + (entries, _observer) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + if (visibleElements.indexOf(entry.target) === -1) { + visibleElements.push(entry.target); + } + } else { + visibleElements = visibleElements.filter((visibleEntry) => { + return visibleEntry !== entry; + }); + } + }); + + if (!hasObserved) { + hideOverlappedSidebars(); + } + hasObserved = true; + }, + {} + ); + els.forEach((el) => { + intersectionObserver.observe(el); + }); + + return { + getVisibleEntries: () => { + return visibleElements; + }, + }; + }; + + const rightElementObserver = visibleItemObserver(rightSideConflictEls); + const leftElementObserver = visibleItemObserver(leftSideConflictEls); + + const hideOverlappedSidebars = () => { + marginScrollVisibility(toRegions(rightElementObserver.getVisibleEntries())); + sidebarScrollVisiblity(toRegions(leftElementObserver.getVisibleEntries())); + if (tocLeftScrollVisibility) { + tocLeftScrollVisibility( + toRegions(leftElementObserver.getVisibleEntries()) + ); + } + }; + + 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; + + const tocOpenDepthStr = tocEl?.getAttribute("data-toc-expanded"); + const tocOpenDepth = tocOpenDepthStr ? Number(tocOpenDepthStr) : 1; + + // 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 (tocOpenDepth === -1 && depth > 1) { + el.classList.add("collapse"); + } else if ( + depth <= tocOpenDepth || + 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/docs/site_libs/quarto-html/tippy.css b/docs/site_libs/quarto-html/tippy.css new file mode 100644 index 0000000..e6ae635 --- /dev/null +++ b/docs/site_libs/quarto-html/tippy.css @@ -0,0 +1 @@ +.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;white-space:normal;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1} \ No newline at end of file diff --git a/docs/site_libs/quarto-html/tippy.umd.min.js b/docs/site_libs/quarto-html/tippy.umd.min.js new file mode 100644 index 0000000..ca292be --- /dev/null +++ b/docs/site_libs/quarto-html/tippy.umd.min.js @@ -0,0 +1,2 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@popperjs/core")):"function"==typeof define&&define.amd?define(["@popperjs/core"],t):(e=e||self).tippy=t(e.Popper)}(this,(function(e){"use strict";var t={passive:!0,capture:!0},n=function(){return document.body};function r(e,t,n){if(Array.isArray(e)){var r=e[t];return null==r?Array.isArray(n)?n[t]:n:r}return e}function o(e,t){var n={}.toString.call(e);return 0===n.indexOf("[object")&&n.indexOf(t+"]")>-1}function i(e,t){return"function"==typeof e?e.apply(void 0,t):e}function a(e,t){return 0===t?e:function(r){clearTimeout(n),n=setTimeout((function(){e(r)}),t)};var n}function s(e,t){var n=Object.assign({},e);return t.forEach((function(e){delete n[e]})),n}function u(e){return[].concat(e)}function c(e,t){-1===e.indexOf(t)&&e.push(t)}function p(e){return e.split("-")[0]}function f(e){return[].slice.call(e)}function l(e){return Object.keys(e).reduce((function(t,n){return void 0!==e[n]&&(t[n]=e[n]),t}),{})}function d(){return document.createElement("div")}function v(e){return["Element","Fragment"].some((function(t){return o(e,t)}))}function m(e){return o(e,"MouseEvent")}function g(e){return!(!e||!e._tippy||e._tippy.reference!==e)}function h(e){return v(e)?[e]:function(e){return o(e,"NodeList")}(e)?f(e):Array.isArray(e)?e:f(document.querySelectorAll(e))}function b(e,t){e.forEach((function(e){e&&(e.style.transitionDuration=t+"ms")}))}function y(e,t){e.forEach((function(e){e&&e.setAttribute("data-state",t)}))}function w(e){var t,n=u(e)[0];return null!=n&&null!=(t=n.ownerDocument)&&t.body?n.ownerDocument:document}function E(e,t,n){var r=t+"EventListener";["transitionend","webkitTransitionEnd"].forEach((function(t){e[r](t,n)}))}function O(e,t){for(var n=t;n;){var r;if(e.contains(n))return!0;n=null==n.getRootNode||null==(r=n.getRootNode())?void 0:r.host}return!1}var x={isTouch:!1},C=0;function T(){x.isTouch||(x.isTouch=!0,window.performance&&document.addEventListener("mousemove",A))}function A(){var e=performance.now();e-C<20&&(x.isTouch=!1,document.removeEventListener("mousemove",A)),C=e}function L(){var e=document.activeElement;if(g(e)){var t=e._tippy;e.blur&&!t.state.isVisible&&e.blur()}}var D=!!("undefined"!=typeof window&&"undefined"!=typeof document)&&!!window.msCrypto,R=Object.assign({appendTo:n,aria:{content:"auto",expanded:"auto"},delay:0,duration:[300,250],getReferenceClientRect:null,hideOnClick:!0,ignoreAttributes:!1,interactive:!1,interactiveBorder:2,interactiveDebounce:0,moveTransition:"",offset:[0,10],onAfterUpdate:function(){},onBeforeUpdate:function(){},onCreate:function(){},onDestroy:function(){},onHidden:function(){},onHide:function(){},onMount:function(){},onShow:function(){},onShown:function(){},onTrigger:function(){},onUntrigger:function(){},onClickOutside:function(){},placement:"top",plugins:[],popperOptions:{},render:null,showOnCreate:!1,touch:!0,trigger:"mouseenter focus",triggerTarget:null},{animateFill:!1,followCursor:!1,inlinePositioning:!1,sticky:!1},{allowHTML:!1,animation:"fade",arrow:!0,content:"",inertia:!1,maxWidth:350,role:"tooltip",theme:"",zIndex:9999}),k=Object.keys(R);function P(e){var t=(e.plugins||[]).reduce((function(t,n){var r,o=n.name,i=n.defaultValue;o&&(t[o]=void 0!==e[o]?e[o]:null!=(r=R[o])?r:i);return t}),{});return Object.assign({},e,t)}function j(e,t){var n=Object.assign({},t,{content:i(t.content,[e])},t.ignoreAttributes?{}:function(e,t){return(t?Object.keys(P(Object.assign({},R,{plugins:t}))):k).reduce((function(t,n){var r=(e.getAttribute("data-tippy-"+n)||"").trim();if(!r)return t;if("content"===n)t[n]=r;else try{t[n]=JSON.parse(r)}catch(e){t[n]=r}return t}),{})}(e,t.plugins));return n.aria=Object.assign({},R.aria,n.aria),n.aria={expanded:"auto"===n.aria.expanded?t.interactive:n.aria.expanded,content:"auto"===n.aria.content?t.interactive?null:"describedby":n.aria.content},n}function M(e,t){e.innerHTML=t}function V(e){var t=d();return!0===e?t.className="tippy-arrow":(t.className="tippy-svg-arrow",v(e)?t.appendChild(e):M(t,e)),t}function I(e,t){v(t.content)?(M(e,""),e.appendChild(t.content)):"function"!=typeof t.content&&(t.allowHTML?M(e,t.content):e.textContent=t.content)}function S(e){var t=e.firstElementChild,n=f(t.children);return{box:t,content:n.find((function(e){return e.classList.contains("tippy-content")})),arrow:n.find((function(e){return e.classList.contains("tippy-arrow")||e.classList.contains("tippy-svg-arrow")})),backdrop:n.find((function(e){return e.classList.contains("tippy-backdrop")}))}}function N(e){var t=d(),n=d();n.className="tippy-box",n.setAttribute("data-state","hidden"),n.setAttribute("tabindex","-1");var r=d();function o(n,r){var o=S(t),i=o.box,a=o.content,s=o.arrow;r.theme?i.setAttribute("data-theme",r.theme):i.removeAttribute("data-theme"),"string"==typeof r.animation?i.setAttribute("data-animation",r.animation):i.removeAttribute("data-animation"),r.inertia?i.setAttribute("data-inertia",""):i.removeAttribute("data-inertia"),i.style.maxWidth="number"==typeof r.maxWidth?r.maxWidth+"px":r.maxWidth,r.role?i.setAttribute("role",r.role):i.removeAttribute("role"),n.content===r.content&&n.allowHTML===r.allowHTML||I(a,e.props),r.arrow?s?n.arrow!==r.arrow&&(i.removeChild(s),i.appendChild(V(r.arrow))):i.appendChild(V(r.arrow)):s&&i.removeChild(s)}return r.className="tippy-content",r.setAttribute("data-state","hidden"),I(r,e.props),t.appendChild(n),n.appendChild(r),o(e.props,e.props),{popper:t,onUpdate:o}}N.$$tippy=!0;var B=1,H=[],U=[];function _(o,s){var v,g,h,C,T,A,L,k,M=j(o,Object.assign({},R,P(l(s)))),V=!1,I=!1,N=!1,_=!1,F=[],W=a(we,M.interactiveDebounce),X=B++,Y=(k=M.plugins).filter((function(e,t){return k.indexOf(e)===t})),$={id:X,reference:o,popper:d(),popperInstance:null,props:M,state:{isEnabled:!0,isVisible:!1,isDestroyed:!1,isMounted:!1,isShown:!1},plugins:Y,clearDelayTimeouts:function(){clearTimeout(v),clearTimeout(g),cancelAnimationFrame(h)},setProps:function(e){if($.state.isDestroyed)return;ae("onBeforeUpdate",[$,e]),be();var t=$.props,n=j(o,Object.assign({},t,l(e),{ignoreAttributes:!0}));$.props=n,he(),t.interactiveDebounce!==n.interactiveDebounce&&(ce(),W=a(we,n.interactiveDebounce));t.triggerTarget&&!n.triggerTarget?u(t.triggerTarget).forEach((function(e){e.removeAttribute("aria-expanded")})):n.triggerTarget&&o.removeAttribute("aria-expanded");ue(),ie(),J&&J(t,n);$.popperInstance&&(Ce(),Ae().forEach((function(e){requestAnimationFrame(e._tippy.popperInstance.forceUpdate)})));ae("onAfterUpdate",[$,e])},setContent:function(e){$.setProps({content:e})},show:function(){var e=$.state.isVisible,t=$.state.isDestroyed,o=!$.state.isEnabled,a=x.isTouch&&!$.props.touch,s=r($.props.duration,0,R.duration);if(e||t||o||a)return;if(te().hasAttribute("disabled"))return;if(ae("onShow",[$],!1),!1===$.props.onShow($))return;$.state.isVisible=!0,ee()&&(z.style.visibility="visible");ie(),de(),$.state.isMounted||(z.style.transition="none");if(ee()){var u=re(),p=u.box,f=u.content;b([p,f],0)}A=function(){var e;if($.state.isVisible&&!_){if(_=!0,z.offsetHeight,z.style.transition=$.props.moveTransition,ee()&&$.props.animation){var t=re(),n=t.box,r=t.content;b([n,r],s),y([n,r],"visible")}se(),ue(),c(U,$),null==(e=$.popperInstance)||e.forceUpdate(),ae("onMount",[$]),$.props.animation&&ee()&&function(e,t){me(e,t)}(s,(function(){$.state.isShown=!0,ae("onShown",[$])}))}},function(){var e,t=$.props.appendTo,r=te();e=$.props.interactive&&t===n||"parent"===t?r.parentNode:i(t,[r]);e.contains(z)||e.appendChild(z);$.state.isMounted=!0,Ce()}()},hide:function(){var e=!$.state.isVisible,t=$.state.isDestroyed,n=!$.state.isEnabled,o=r($.props.duration,1,R.duration);if(e||t||n)return;if(ae("onHide",[$],!1),!1===$.props.onHide($))return;$.state.isVisible=!1,$.state.isShown=!1,_=!1,V=!1,ee()&&(z.style.visibility="hidden");if(ce(),ve(),ie(!0),ee()){var i=re(),a=i.box,s=i.content;$.props.animation&&(b([a,s],o),y([a,s],"hidden"))}se(),ue(),$.props.animation?ee()&&function(e,t){me(e,(function(){!$.state.isVisible&&z.parentNode&&z.parentNode.contains(z)&&t()}))}(o,$.unmount):$.unmount()},hideWithInteractivity:function(e){ne().addEventListener("mousemove",W),c(H,W),W(e)},enable:function(){$.state.isEnabled=!0},disable:function(){$.hide(),$.state.isEnabled=!1},unmount:function(){$.state.isVisible&&$.hide();if(!$.state.isMounted)return;Te(),Ae().forEach((function(e){e._tippy.unmount()})),z.parentNode&&z.parentNode.removeChild(z);U=U.filter((function(e){return e!==$})),$.state.isMounted=!1,ae("onHidden",[$])},destroy:function(){if($.state.isDestroyed)return;$.clearDelayTimeouts(),$.unmount(),be(),delete o._tippy,$.state.isDestroyed=!0,ae("onDestroy",[$])}};if(!M.render)return $;var q=M.render($),z=q.popper,J=q.onUpdate;z.setAttribute("data-tippy-root",""),z.id="tippy-"+$.id,$.popper=z,o._tippy=$,z._tippy=$;var G=Y.map((function(e){return e.fn($)})),K=o.hasAttribute("aria-expanded");return he(),ue(),ie(),ae("onCreate",[$]),M.showOnCreate&&Le(),z.addEventListener("mouseenter",(function(){$.props.interactive&&$.state.isVisible&&$.clearDelayTimeouts()})),z.addEventListener("mouseleave",(function(){$.props.interactive&&$.props.trigger.indexOf("mouseenter")>=0&&ne().addEventListener("mousemove",W)})),$;function Q(){var e=$.props.touch;return Array.isArray(e)?e:[e,0]}function Z(){return"hold"===Q()[0]}function ee(){var e;return!(null==(e=$.props.render)||!e.$$tippy)}function te(){return L||o}function ne(){var e=te().parentNode;return e?w(e):document}function re(){return S(z)}function oe(e){return $.state.isMounted&&!$.state.isVisible||x.isTouch||C&&"focus"===C.type?0:r($.props.delay,e?0:1,R.delay)}function ie(e){void 0===e&&(e=!1),z.style.pointerEvents=$.props.interactive&&!e?"":"none",z.style.zIndex=""+$.props.zIndex}function ae(e,t,n){var r;(void 0===n&&(n=!0),G.forEach((function(n){n[e]&&n[e].apply(n,t)})),n)&&(r=$.props)[e].apply(r,t)}function se(){var e=$.props.aria;if(e.content){var t="aria-"+e.content,n=z.id;u($.props.triggerTarget||o).forEach((function(e){var r=e.getAttribute(t);if($.state.isVisible)e.setAttribute(t,r?r+" "+n:n);else{var o=r&&r.replace(n,"").trim();o?e.setAttribute(t,o):e.removeAttribute(t)}}))}}function ue(){!K&&$.props.aria.expanded&&u($.props.triggerTarget||o).forEach((function(e){$.props.interactive?e.setAttribute("aria-expanded",$.state.isVisible&&e===te()?"true":"false"):e.removeAttribute("aria-expanded")}))}function ce(){ne().removeEventListener("mousemove",W),H=H.filter((function(e){return e!==W}))}function pe(e){if(!x.isTouch||!N&&"mousedown"!==e.type){var t=e.composedPath&&e.composedPath()[0]||e.target;if(!$.props.interactive||!O(z,t)){if(u($.props.triggerTarget||o).some((function(e){return O(e,t)}))){if(x.isTouch)return;if($.state.isVisible&&$.props.trigger.indexOf("click")>=0)return}else ae("onClickOutside",[$,e]);!0===$.props.hideOnClick&&($.clearDelayTimeouts(),$.hide(),I=!0,setTimeout((function(){I=!1})),$.state.isMounted||ve())}}}function fe(){N=!0}function le(){N=!1}function de(){var e=ne();e.addEventListener("mousedown",pe,!0),e.addEventListener("touchend",pe,t),e.addEventListener("touchstart",le,t),e.addEventListener("touchmove",fe,t)}function ve(){var e=ne();e.removeEventListener("mousedown",pe,!0),e.removeEventListener("touchend",pe,t),e.removeEventListener("touchstart",le,t),e.removeEventListener("touchmove",fe,t)}function me(e,t){var n=re().box;function r(e){e.target===n&&(E(n,"remove",r),t())}if(0===e)return t();E(n,"remove",T),E(n,"add",r),T=r}function ge(e,t,n){void 0===n&&(n=!1),u($.props.triggerTarget||o).forEach((function(r){r.addEventListener(e,t,n),F.push({node:r,eventType:e,handler:t,options:n})}))}function he(){var e;Z()&&(ge("touchstart",ye,{passive:!0}),ge("touchend",Ee,{passive:!0})),(e=$.props.trigger,e.split(/\s+/).filter(Boolean)).forEach((function(e){if("manual"!==e)switch(ge(e,ye),e){case"mouseenter":ge("mouseleave",Ee);break;case"focus":ge(D?"focusout":"blur",Oe);break;case"focusin":ge("focusout",Oe)}}))}function be(){F.forEach((function(e){var t=e.node,n=e.eventType,r=e.handler,o=e.options;t.removeEventListener(n,r,o)})),F=[]}function ye(e){var t,n=!1;if($.state.isEnabled&&!xe(e)&&!I){var r="focus"===(null==(t=C)?void 0:t.type);C=e,L=e.currentTarget,ue(),!$.state.isVisible&&m(e)&&H.forEach((function(t){return t(e)})),"click"===e.type&&($.props.trigger.indexOf("mouseenter")<0||V)&&!1!==$.props.hideOnClick&&$.state.isVisible?n=!0:Le(e),"click"===e.type&&(V=!n),n&&!r&&De(e)}}function we(e){var t=e.target,n=te().contains(t)||z.contains(t);"mousemove"===e.type&&n||function(e,t){var n=t.clientX,r=t.clientY;return e.every((function(e){var t=e.popperRect,o=e.popperState,i=e.props.interactiveBorder,a=p(o.placement),s=o.modifiersData.offset;if(!s)return!0;var u="bottom"===a?s.top.y:0,c="top"===a?s.bottom.y:0,f="right"===a?s.left.x:0,l="left"===a?s.right.x:0,d=t.top-r+u>i,v=r-t.bottom-c>i,m=t.left-n+f>i,g=n-t.right-l>i;return d||v||m||g}))}(Ae().concat(z).map((function(e){var t,n=null==(t=e._tippy.popperInstance)?void 0:t.state;return n?{popperRect:e.getBoundingClientRect(),popperState:n,props:M}:null})).filter(Boolean),e)&&(ce(),De(e))}function Ee(e){xe(e)||$.props.trigger.indexOf("click")>=0&&V||($.props.interactive?$.hideWithInteractivity(e):De(e))}function Oe(e){$.props.trigger.indexOf("focusin")<0&&e.target!==te()||$.props.interactive&&e.relatedTarget&&z.contains(e.relatedTarget)||De(e)}function xe(e){return!!x.isTouch&&Z()!==e.type.indexOf("touch")>=0}function Ce(){Te();var t=$.props,n=t.popperOptions,r=t.placement,i=t.offset,a=t.getReferenceClientRect,s=t.moveTransition,u=ee()?S(z).arrow:null,c=a?{getBoundingClientRect:a,contextElement:a.contextElement||te()}:o,p=[{name:"offset",options:{offset:i}},{name:"preventOverflow",options:{padding:{top:2,bottom:2,left:5,right:5}}},{name:"flip",options:{padding:5}},{name:"computeStyles",options:{adaptive:!s}},{name:"$$tippy",enabled:!0,phase:"beforeWrite",requires:["computeStyles"],fn:function(e){var t=e.state;if(ee()){var n=re().box;["placement","reference-hidden","escaped"].forEach((function(e){"placement"===e?n.setAttribute("data-placement",t.placement):t.attributes.popper["data-popper-"+e]?n.setAttribute("data-"+e,""):n.removeAttribute("data-"+e)})),t.attributes.popper={}}}}];ee()&&u&&p.push({name:"arrow",options:{element:u,padding:3}}),p.push.apply(p,(null==n?void 0:n.modifiers)||[]),$.popperInstance=e.createPopper(c,z,Object.assign({},n,{placement:r,onFirstUpdate:A,modifiers:p}))}function Te(){$.popperInstance&&($.popperInstance.destroy(),$.popperInstance=null)}function Ae(){return f(z.querySelectorAll("[data-tippy-root]"))}function Le(e){$.clearDelayTimeouts(),e&&ae("onTrigger",[$,e]),de();var t=oe(!0),n=Q(),r=n[0],o=n[1];x.isTouch&&"hold"===r&&o&&(t=o),t?v=setTimeout((function(){$.show()}),t):$.show()}function De(e){if($.clearDelayTimeouts(),ae("onUntrigger",[$,e]),$.state.isVisible){if(!($.props.trigger.indexOf("mouseenter")>=0&&$.props.trigger.indexOf("click")>=0&&["mouseleave","mousemove"].indexOf(e.type)>=0&&V)){var t=oe(!1);t?g=setTimeout((function(){$.state.isVisible&&$.hide()}),t):h=requestAnimationFrame((function(){$.hide()}))}}else ve()}}function F(e,n){void 0===n&&(n={});var r=R.plugins.concat(n.plugins||[]);document.addEventListener("touchstart",T,t),window.addEventListener("blur",L);var o=Object.assign({},n,{plugins:r}),i=h(e).reduce((function(e,t){var n=t&&_(t,o);return n&&e.push(n),e}),[]);return v(e)?i[0]:i}F.defaultProps=R,F.setDefaultProps=function(e){Object.keys(e).forEach((function(t){R[t]=e[t]}))},F.currentInput=x;var W=Object.assign({},e.applyStyles,{effect:function(e){var t=e.state,n={popper:{position:t.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};Object.assign(t.elements.popper.style,n.popper),t.styles=n,t.elements.arrow&&Object.assign(t.elements.arrow.style,n.arrow)}}),X={mouseover:"mouseenter",focusin:"focus",click:"click"};var Y={name:"animateFill",defaultValue:!1,fn:function(e){var t;if(null==(t=e.props.render)||!t.$$tippy)return{};var n=S(e.popper),r=n.box,o=n.content,i=e.props.animateFill?function(){var e=d();return e.className="tippy-backdrop",y([e],"hidden"),e}():null;return{onCreate:function(){i&&(r.insertBefore(i,r.firstElementChild),r.setAttribute("data-animatefill",""),r.style.overflow="hidden",e.setProps({arrow:!1,animation:"shift-away"}))},onMount:function(){if(i){var e=r.style.transitionDuration,t=Number(e.replace("ms",""));o.style.transitionDelay=Math.round(t/10)+"ms",i.style.transitionDuration=e,y([i],"visible")}},onShow:function(){i&&(i.style.transitionDuration="0ms")},onHide:function(){i&&y([i],"hidden")}}}};var $={clientX:0,clientY:0},q=[];function z(e){var t=e.clientX,n=e.clientY;$={clientX:t,clientY:n}}var J={name:"followCursor",defaultValue:!1,fn:function(e){var t=e.reference,n=w(e.props.triggerTarget||t),r=!1,o=!1,i=!0,a=e.props;function s(){return"initial"===e.props.followCursor&&e.state.isVisible}function u(){n.addEventListener("mousemove",f)}function c(){n.removeEventListener("mousemove",f)}function p(){r=!0,e.setProps({getReferenceClientRect:null}),r=!1}function f(n){var r=!n.target||t.contains(n.target),o=e.props.followCursor,i=n.clientX,a=n.clientY,s=t.getBoundingClientRect(),u=i-s.left,c=a-s.top;!r&&e.props.interactive||e.setProps({getReferenceClientRect:function(){var e=t.getBoundingClientRect(),n=i,r=a;"initial"===o&&(n=e.left+u,r=e.top+c);var s="horizontal"===o?e.top:r,p="vertical"===o?e.right:n,f="horizontal"===o?e.bottom:r,l="vertical"===o?e.left:n;return{width:p-l,height:f-s,top:s,right:p,bottom:f,left:l}}})}function l(){e.props.followCursor&&(q.push({instance:e,doc:n}),function(e){e.addEventListener("mousemove",z)}(n))}function d(){0===(q=q.filter((function(t){return t.instance!==e}))).filter((function(e){return e.doc===n})).length&&function(e){e.removeEventListener("mousemove",z)}(n)}return{onCreate:l,onDestroy:d,onBeforeUpdate:function(){a=e.props},onAfterUpdate:function(t,n){var i=n.followCursor;r||void 0!==i&&a.followCursor!==i&&(d(),i?(l(),!e.state.isMounted||o||s()||u()):(c(),p()))},onMount:function(){e.props.followCursor&&!o&&(i&&(f($),i=!1),s()||u())},onTrigger:function(e,t){m(t)&&($={clientX:t.clientX,clientY:t.clientY}),o="focus"===t.type},onHidden:function(){e.props.followCursor&&(p(),c(),i=!0)}}}};var G={name:"inlinePositioning",defaultValue:!1,fn:function(e){var t,n=e.reference;var r=-1,o=!1,i=[],a={name:"tippyInlinePositioning",enabled:!0,phase:"afterWrite",fn:function(o){var a=o.state;e.props.inlinePositioning&&(-1!==i.indexOf(a.placement)&&(i=[]),t!==a.placement&&-1===i.indexOf(a.placement)&&(i.push(a.placement),e.setProps({getReferenceClientRect:function(){return function(e){return function(e,t,n,r){if(n.length<2||null===e)return t;if(2===n.length&&r>=0&&n[0].left>n[1].right)return n[r]||t;switch(e){case"top":case"bottom":var o=n[0],i=n[n.length-1],a="top"===e,s=o.top,u=i.bottom,c=a?o.left:i.left,p=a?o.right:i.right;return{top:s,bottom:u,left:c,right:p,width:p-c,height:u-s};case"left":case"right":var f=Math.min.apply(Math,n.map((function(e){return e.left}))),l=Math.max.apply(Math,n.map((function(e){return e.right}))),d=n.filter((function(t){return"left"===e?t.left===f:t.right===l})),v=d[0].top,m=d[d.length-1].bottom;return{top:v,bottom:m,left:f,right:l,width:l-f,height:m-v};default:return t}}(p(e),n.getBoundingClientRect(),f(n.getClientRects()),r)}(a.placement)}})),t=a.placement)}};function s(){var t;o||(t=function(e,t){var n;return{popperOptions:Object.assign({},e.popperOptions,{modifiers:[].concat(((null==(n=e.popperOptions)?void 0:n.modifiers)||[]).filter((function(e){return e.name!==t.name})),[t])})}}(e.props,a),o=!0,e.setProps(t),o=!1)}return{onCreate:s,onAfterUpdate:s,onTrigger:function(t,n){if(m(n)){var o=f(e.reference.getClientRects()),i=o.find((function(e){return e.left-2<=n.clientX&&e.right+2>=n.clientX&&e.top-2<=n.clientY&&e.bottom+2>=n.clientY})),a=o.indexOf(i);r=a>-1?a:r}},onHidden:function(){r=-1}}}};var K={name:"sticky",defaultValue:!1,fn:function(e){var t=e.reference,n=e.popper;function r(t){return!0===e.props.sticky||e.props.sticky===t}var o=null,i=null;function a(){var s=r("reference")?(e.popperInstance?e.popperInstance.state.elements.reference:t).getBoundingClientRect():null,u=r("popper")?n.getBoundingClientRect():null;(s&&Q(o,s)||u&&Q(i,u))&&e.popperInstance&&e.popperInstance.update(),o=s,i=u,e.state.isMounted&&requestAnimationFrame(a)}return{onMount:function(){e.props.sticky&&a()}}}};function Q(e,t){return!e||!t||(e.top!==t.top||e.right!==t.right||e.bottom!==t.bottom||e.left!==t.left)}return F.setDefaultProps({plugins:[Y,J,G,K],render:N}),F.createSingleton=function(e,t){var n;void 0===t&&(t={});var r,o=e,i=[],a=[],c=t.overrides,p=[],f=!1;function l(){a=o.map((function(e){return u(e.props.triggerTarget||e.reference)})).reduce((function(e,t){return e.concat(t)}),[])}function v(){i=o.map((function(e){return e.reference}))}function m(e){o.forEach((function(t){e?t.enable():t.disable()}))}function g(e){return o.map((function(t){var n=t.setProps;return t.setProps=function(o){n(o),t.reference===r&&e.setProps(o)},function(){t.setProps=n}}))}function h(e,t){var n=a.indexOf(t);if(t!==r){r=t;var s=(c||[]).concat("content").reduce((function(e,t){return e[t]=o[n].props[t],e}),{});e.setProps(Object.assign({},s,{getReferenceClientRect:"function"==typeof s.getReferenceClientRect?s.getReferenceClientRect:function(){var e;return null==(e=i[n])?void 0:e.getBoundingClientRect()}}))}}m(!1),v(),l();var b={fn:function(){return{onDestroy:function(){m(!0)},onHidden:function(){r=null},onClickOutside:function(e){e.props.showOnCreate&&!f&&(f=!0,r=null)},onShow:function(e){e.props.showOnCreate&&!f&&(f=!0,h(e,i[0]))},onTrigger:function(e,t){h(e,t.currentTarget)}}}},y=F(d(),Object.assign({},s(t,["overrides"]),{plugins:[b].concat(t.plugins||[]),triggerTarget:a,popperOptions:Object.assign({},t.popperOptions,{modifiers:[].concat((null==(n=t.popperOptions)?void 0:n.modifiers)||[],[W])})})),w=y.show;y.show=function(e){if(w(),!r&&null==e)return h(y,i[0]);if(!r||null!=e){if("number"==typeof e)return i[e]&&h(y,i[e]);if(o.indexOf(e)>=0){var t=e.reference;return h(y,t)}return i.indexOf(e)>=0?h(y,e):void 0}},y.showNext=function(){var e=i[0];if(!r)return y.show(0);var t=i.indexOf(r);y.show(i[t+1]||e)},y.showPrevious=function(){var e=i[i.length-1];if(!r)return y.show(e);var t=i.indexOf(r),n=i[t-1]||e;y.show(n)};var E=y.setProps;return y.setProps=function(e){c=e.overrides||c,E(e)},y.setInstances=function(e){m(!0),p.forEach((function(e){return e()})),o=e,m(!1),v(),l(),p=g(y),y.setProps({triggerTarget:a})},p=g(y),y},F.delegate=function(e,n){var r=[],o=[],i=!1,a=n.target,c=s(n,["target"]),p=Object.assign({},c,{trigger:"manual",touch:!1}),f=Object.assign({touch:R.touch},c,{showOnCreate:!0}),l=F(e,p);function d(e){if(e.target&&!i){var t=e.target.closest(a);if(t){var r=t.getAttribute("data-tippy-trigger")||n.trigger||R.trigger;if(!t._tippy&&!("touchstart"===e.type&&"boolean"==typeof f.touch||"touchstart"!==e.type&&r.indexOf(X[e.type])<0)){var s=F(t,f);s&&(o=o.concat(s))}}}}function v(e,t,n,o){void 0===o&&(o=!1),e.addEventListener(t,n,o),r.push({node:e,eventType:t,handler:n,options:o})}return u(l).forEach((function(e){var n=e.destroy,a=e.enable,s=e.disable;e.destroy=function(e){void 0===e&&(e=!0),e&&o.forEach((function(e){e.destroy()})),o=[],r.forEach((function(e){var t=e.node,n=e.eventType,r=e.handler,o=e.options;t.removeEventListener(n,r,o)})),r=[],n()},e.enable=function(){a(),o.forEach((function(e){return e.enable()})),i=!1},e.disable=function(){s(),o.forEach((function(e){return e.disable()})),i=!0},function(e){var n=e.reference;v(n,"touchstart",d,t),v(n,"mouseover",d),v(n,"focusin",d),v(n,"click",d)}(e)})),l},F.hideAll=function(e){var t=void 0===e?{}:e,n=t.exclude,r=t.duration;U.forEach((function(e){var t=!1;if(n&&(t=g(n)?e.reference===n:e.popper===n.popper),!t){var o=e.props.duration;e.setProps({duration:r}),e.hide(),e.state.isDestroyed||e.setProps({duration:o})}}))},F.roundArrow='',F})); + diff --git a/docs/site_libs/quarto-nav/quarto-nav.js b/docs/site_libs/quarto-nav/quarto-nav.js new file mode 100644 index 0000000..ebfc262 --- /dev/null +++ b/docs/site_libs/quarto-nav/quarto-nav.js @@ -0,0 +1,288 @@ +const headroomChanged = new CustomEvent("quarto-hrChanged", { + detail: {}, + bubbles: true, + cancelable: false, + composed: false, +}); + +window.document.addEventListener("DOMContentLoaded", function () { + let init = false; + + // Manage the back to top button, if one is present. + let lastScrollTop = window.pageYOffset || document.documentElement.scrollTop; + const scrollDownBuffer = 5; + const scrollUpBuffer = 35; + const btn = document.getElementById("quarto-back-to-top"); + const hideBackToTop = () => { + btn.style.display = "none"; + }; + const showBackToTop = () => { + btn.style.display = "inline-block"; + }; + if (btn) { + window.document.addEventListener( + "scroll", + function () { + const currentScrollTop = + window.pageYOffset || document.documentElement.scrollTop; + + // Shows and hides the button 'intelligently' as the user scrolls + if (currentScrollTop - scrollDownBuffer > lastScrollTop) { + hideBackToTop(); + lastScrollTop = currentScrollTop <= 0 ? 0 : currentScrollTop; + } else if (currentScrollTop < lastScrollTop - scrollUpBuffer) { + showBackToTop(); + lastScrollTop = currentScrollTop <= 0 ? 0 : currentScrollTop; + } + + // Show the button at the bottom, hides it at the top + if (currentScrollTop <= 0) { + hideBackToTop(); + } else if ( + window.innerHeight + currentScrollTop >= + document.body.offsetHeight + ) { + showBackToTop(); + } + }, + 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 dashboardOffset() { + const dashboardNavEl = window.document.getElementById( + "quarto-dashboard-header" + ); + if (dashboardNavEl !== null) { + return dashboardNavEl.clientHeight; + } else { + return 0; + } + } + + function updateDocumentOffsetWithoutAnimation() { + updateDocumentOffset(false); + } + + function updateDocumentOffset(animated) { + // set body offset + const topOffset = headerOffset(); + const bodyOffset = topOffset + footerOffset() + dashboardOffset(); + 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; + } + }; + } + + window.addEventListener( + "hashchange", + function (e) { + if ( + getComputedStyle(document.documentElement).scrollBehavior !== "smooth" + ) { + window.scrollTo(0, window.pageYOffset - headerOffset()); + } + }, + false + ); + + // Observe size changed for the header + const headerEl = window.document.querySelector("header.fixed-top"); + if (headerEl && window.ResizeObserver) { + const observer = new window.ResizeObserver(() => { + setTimeout(updateDocumentOffsetWithoutAnimation, 0); + }); + 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++) { + if (links[i].href) { + 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, a.quarto-navigation-tool, a.quarto-navbar-tools, a.quarto-navbar-tools-item" + ); + for (let i = 0; i < sharingLinks.length; i++) { + const sharingLink = sharingLinks[i]; + const href = sharingLink.getAttribute("href"); + if (href) { + sharingLink.setAttribute( + "href", + href.replace("|url|", window.location.href) + ); + } + } + + // Scroll the active navigation item into view, if necessary + const navSidebar = window.document.querySelector("nav#quarto-sidebar"); + if (navSidebar) { + // Find the active item + const activeItem = navSidebar.querySelector("li.sidebar-item a.active"); + if (activeItem) { + // Wait for the scroll height and height to resolve by observing size changes on the + // nav element that is scrollable + const resizeObserver = new ResizeObserver((_entries) => { + // The bottom of the element + const elBottom = activeItem.offsetTop; + const viewBottom = navSidebar.scrollTop + navSidebar.clientHeight; + + // The element height and scroll height are the same, then we are still loading + if (viewBottom !== navSidebar.scrollHeight) { + // Determine if the item isn't visible and scroll to it + if (elBottom >= viewBottom) { + navSidebar.scrollTop = elBottom; + } + + // stop observing now since we've completed the scroll + resizeObserver.unobserve(navSidebar); + } + }); + resizeObserver.observe(navSidebar); + } + } + } +}); diff --git a/docs/site_libs/quarto-search/autocomplete.umd.js b/docs/site_libs/quarto-search/autocomplete.umd.js new file mode 100644 index 0000000..ae0063a --- /dev/null +++ b/docs/site_libs/quarto-search/autocomplete.umd.js @@ -0,0 +1,3 @@ +/*! @algolia/autocomplete-js 1.11.1 | 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,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var r,o,i,u,a=[],l=!0,c=!1;try{if(i=(n=n.call(e)).next,0===t){if(Object(n)!==n)return;l=!1}else for(;!(l=(r=i.call(n)).done)&&(a.push(r.value),a.length!==t);l=!0);}catch(e){c=!0,o=e}finally{try{if(!l&&null!=n.return&&(u=n.return(),Object(u)!==u))return}finally{if(c)throw o}}return a}}(e,t)||c(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function l(e){return function(e){if(Array.isArray(e))return s(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||c(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){if(e){if("string"==typeof e)return s(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?s(e,t):void 0}}function s(e,t){(null==t||t>e.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);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}function x(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 t=1;t1&&void 0!==arguments[1]?arguments[1]:20,n=[],r=0;r=3||2===n&&r>=4||1===n&&r>=10);function i(t,n,r){if(o&&void 0!==r){var i=r[0].__autocomplete_algoliaCredentials,u={"X-Algolia-Application-Id":i.appId,"X-Algolia-API-Key":i.apiKey};e.apply(void 0,[t].concat(D(n),[{headers:u}]))}else e.apply(void 0,[t].concat(D(n)))}return{init:function(t,n){e("init",{appId:t,apiKey:n})},setUserToken:function(t){e("setUserToken",t)},clickedObjectIDsAfterSearch:function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&i("clickedObjectIDsAfterSearch",B(t),t[0].items)},clickedObjectIDs:function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&i("clickedObjectIDs",B(t),t[0].items)},clickedFilters:function(){for(var t=arguments.length,n=new Array(t),r=0;r0&&e.apply(void 0,["clickedFilters"].concat(n))},convertedObjectIDsAfterSearch:function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&i("convertedObjectIDsAfterSearch",B(t),t[0].items)},convertedObjectIDs:function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&i("convertedObjectIDs",B(t),t[0].items)},convertedFilters:function(){for(var t=arguments.length,n=new Array(t),r=0;r0&&e.apply(void 0,["convertedFilters"].concat(n))},viewedObjectIDs:function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&t.reduce((function(e,t){var n=t.items,r=k(t,A);return[].concat(D(e),D(q(N(N({},r),{},{objectIDs:(null==n?void 0:n.map((function(e){return e.objectID})))||r.objectIDs})).map((function(e){return{items:n,payload:e}}))))}),[]).forEach((function(e){var t=e.items;return i("viewedObjectIDs",[e.payload],t)}))},viewedFilters:function(){for(var t=arguments.length,n=new Array(t),r=0;r0&&e.apply(void 0,["viewedFilters"].concat(n))}}}function F(e){var t=e.items.reduce((function(e,t){var n;return e[t.__autocomplete_indexName]=(null!==(n=e[t.__autocomplete_indexName])&&void 0!==n?n:[]).concat(t),e}),{});return Object.keys(t).map((function(e){return{index:e,items:t[e],algoliaSource:["autocomplete"]}}))}function L(e){return e.objectID&&e.__autocomplete_indexName&&e.__autocomplete_queryID}function U(e){return U="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},U(e)}function M(e){return function(e){if(Array.isArray(e))return H(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 H(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 H(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 H(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n0&&z({onItemsChange:r,items:n,insights:a,state:t}))}}),0);return{name:"aa.algoliaInsightsPlugin",subscribe:function(e){var t=e.setContext,n=e.onSelect,r=e.onActive;function l(e){t({algoliaInsightsPlugin:{__algoliaSearchParameters:W({clickAnalytics:!0},e?{userToken:e}:{}),insights:a}})}u("addAlgoliaAgent","insights-plugin"),l(),u("onUserTokenChange",l),u("getUserToken",null,(function(e,t){l(t)})),n((function(e){var t=e.item,n=e.state,r=e.event,i=e.source;L(t)&&o({state:n,event:r,insights:a,item:t,insightsEvents:[W({eventName:"Item Selected"},j({item:t,items:i.getItems().filter(L)}))]})})),r((function(e){var t=e.item,n=e.source,r=e.state,o=e.event;L(t)&&i({state:r,event:o,insights:a,item:t,insightsEvents:[W({eventName:"Item Active"},j({item:t,items:n.getItems().filter(L)}))]})}))},onStateChange:function(e){var t=e.state;c({state:t})},__autocomplete_pluginOptions:e}}function J(e,t){var n=t;return{then:function(t,r){return J(e.then(Y(t,n,e),Y(r,n,e)),n)},catch:function(t){return J(e.catch(Y(t,n,e)),n)},finally:function(t){return t&&n.onCancelList.push(t),J(e.finally(Y(t&&function(){return n.onCancelList=[],t()},n,e)),n)},cancel:function(){n.isCanceled=!0;var e=n.onCancelList;n.onCancelList=[],e.forEach((function(e){e()}))},isCanceled:function(){return!0===n.isCanceled}}}function X(e){return J(e,{isCanceled:!1,onCancelList:[]})}function Y(e,t,n){return e?function(n){return t.isCanceled?n:e(n)}:n}function Z(e,t,n,r){if(!n)return null;if(e<0&&(null===t||null!==r&&0===t))return n+e;var o=(null===t?-1:t)+e;return o<=-1||o>=n?null===r?null:0:o}function ee(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 te(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=new Array(t);n0},reshape:function(e){return e.sources}},e),{},{id:null!==(n=e.id)&&void 0!==n?n:d(),plugins:o,initialState:he({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(ye(o.map((function(e){return e.getSources}))),[e.getSources]).filter(Boolean).map((function(e){return function(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={getItemInputValue:function(e){return e.state.query},getItemUrl:function(){},onSelect:function(e){(0,e.setIsOpen)(!1)},onActive:O,onResolve:O};Object.keys(t).forEach((function(e){t[e].__default=!0}));var r=te(te({},t),e);return Promise.resolve(r)})))}))}(e,n)}))).then((function(e){return m(e)})).then((function(e){return e.map((function(e){return he(he({},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)}))},onResolve:function(n){e.onResolve(n),t.forEach((function(e){var t;return null===(t=e.onResolve)||void 0===t?void 0:t.call(e,n)}))}})}))}))},navigator:he({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)})}function Se(e){return Se="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Se(e)}function je(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 Pe(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}var He,Ve,We,Ke=null,Qe=(He=-1,Ve=-1,We=void 0,function(e){var t=++He;return Promise.resolve(e).then((function(e){return We&&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 et(e){return et="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},et(e)}var tt=["props","refresh","store"],nt=["inputElement","formElement","panelElement"],rt=["inputElement"],ot=["inputElement","maxLength"],it=["source"],ut=["item","source"];function at(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 lt(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 ft(e){var t=e.props,n=e.refresh,r=e.store,o=st(e,tt);return{getEnvironmentProps:function(e){var n=e.inputElement,o=e.formElement,i=e.panelElement;function u(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())}return lt({onTouchStart:u,onMouseDown:u,onTouchMove:function(e){!1!==r.getState().isOpen&&n===t.environment.document.activeElement&&e.target!==n&&n.blur()}},st(e,nt))},getRootProps:function(e){return lt({role:"combobox","aria-expanded":r.getState().isOpen,"aria-haspopup":"listbox","aria-owns":r.getState().isOpen?r.getState().collections.map((function(e){var n=e.source;return ie(t.id,"list",n)})).join(" "):void 0,"aria-labelledby":ie(t.id,"label")},e)},getFormProps:function(e){return e.inputElement,lt({action:"",noValidate:!0,role:"search",onSubmit:function(i){var u;i.preventDefault(),t.onSubmit(lt({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(lt({event:i,refresh:n,state:r.getState()},o)),r.dispatch("reset",null),null===(u=e.inputElement)||void 0===u||u.focus()}},st(e,rt))},getLabelProps:function(e){return lt({htmlFor:ie(t.id,"input"),id:ie(t.id,"label")},e)},getInputProps:function(e){var i;function u(e){(t.openOnFocus||Boolean(r.getState().query))&&$e(lt({event:e,props:t,query:r.getState().completion||r.getState().query,refresh:n,store:r},o)),r.dispatch("focus",null)}var a=e||{};a.inputElement;var l=a.maxLength,c=void 0===l?512:l,s=st(a,ot),f=oe(r.getState()),p=function(e){return Boolean(e&&e.match(ue))}((null===(i=t.environment.navigator)||void 0===i?void 0:i.userAgent)||""),m=t.enterKeyHint||(null!=f&&f.itemUrl&&!p?"go":"search");return lt({"aria-autocomplete":"both","aria-activedescendant":r.getState().isOpen&&null!==r.getState().activeItemId?ie(t.id,"item-".concat(r.getState().activeItemId),null==f?void 0:f.source):void 0,"aria-controls":r.getState().isOpen?r.getState().collections.map((function(e){var n=e.source;return ie(t.id,"list",n)})).join(" "):void 0,"aria-labelledby":ie(t.id,"label"),value:r.getState().completion||r.getState().query,id:ie(t.id,"input"),autoComplete:"off",autoCorrect:"off",autoCapitalize:"off",enterKeyHint:m,spellCheck:"false",autoFocus:t.autoFocus,placeholder:t.placeholder,maxLength:c,type:"search",onChange:function(e){$e(lt({event:e,props:t,query:e.currentTarget.value.slice(0,c),refresh:n,store:r},o))},onKeyDown:function(e){!function(e){var t=e.event,n=e.props,r=e.refresh,o=e.store,i=Ze(e,Ge);if("ArrowUp"===t.key||"ArrowDown"===t.key){var u=function(){var e=oe(o.getState()),t=n.environment.document.getElementById(ie(n.id,"item-".concat(o.getState().activeItemId),null==e?void 0:e.source));t&&(t.scrollIntoViewIfNeeded?t.scrollIntoViewIfNeeded(!1):t.scrollIntoView(!1))},a=function(){var e=oe(o.getState());if(null!==o.getState().activeItemId&&e){var n=e.item,u=e.itemInputValue,a=e.itemUrl,l=e.source;l.onActive(Xe({event:t,item:n,itemInputValue:u,itemUrl:a,refresh:r,source:l,state:o.getState()},i))}};t.preventDefault(),!1===o.getState().isOpen&&(n.openOnFocus||Boolean(o.getState().query))?$e(Xe({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("Tab"===t.key)o.dispatch("blur",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 void(n.debug||o.pendingRequests.cancelAll());t.preventDefault();var l=oe(o.getState()),c=l.item,s=l.itemInputValue,f=l.itemUrl,p=l.source;if(t.metaKey||t.ctrlKey)void 0!==f&&(p.onSelect(Xe({event:t,item:c,itemInputValue:s,itemUrl:f,refresh:r,source:p,state:o.getState()},i)),n.navigator.navigateNewTab({itemUrl:f,item:c,state:o.getState()}));else if(t.shiftKey)void 0!==f&&(p.onSelect(Xe({event:t,item:c,itemInputValue:s,itemUrl:f,refresh:r,source:p,state:o.getState()},i)),n.navigator.navigateNewWindow({itemUrl:f,item:c,state:o.getState()}));else if(t.altKey);else{if(void 0!==f)return p.onSelect(Xe({event:t,item:c,itemInputValue:s,itemUrl:f,refresh:r,source:p,state:o.getState()},i)),void n.navigator.navigate({itemUrl:f,item:c,state:o.getState()});$e(Xe({event:t,nextState:{isOpen:!1},props:n,query:s,refresh:r,store:o},i)).then((function(){p.onSelect(Xe({event:t,item:c,itemInputValue:s,itemUrl:f,refresh:r,source:p,state:o.getState()},i))}))}}}(lt({event:e,props:t,refresh:n,store:r},o))},onFocus:u,onBlur:O,onClick:function(n){e.inputElement!==t.environment.document.activeElement||r.getState().isOpen||u(n)}},s)},getPanelProps:function(e){return lt({onMouseDown:function(e){e.preventDefault()},onMouseLeave:function(){r.dispatch("mouseleave",null)}},e)},getListProps:function(e){var n=e||{},r=n.source,o=st(n,it);return lt({role:"listbox","aria-labelledby":ie(t.id,"label"),id:ie(t.id,"list",r)},o)},getItemProps:function(e){var i=e.item,u=e.source,a=st(e,ut);return lt({id:ie(t.id,"item-".concat(i.__autocomplete_id),u),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=oe(r.getState());if(null!==r.getState().activeItemId&&t){var u=t.item,a=t.itemInputValue,l=t.itemUrl,c=t.source;c.onActive(lt({event:e,item:u,itemInputValue:a,itemUrl:l,refresh:n,source:c,state:r.getState()},o))}}},onMouseDown:function(e){e.preventDefault()},onClick:function(e){var a=u.getItemInputValue({item:i,state:r.getState()}),l=u.getItemUrl({item:i,state:r.getState()});(l?Promise.resolve():$e(lt({event:e,nextState:{isOpen:!1},props:t,query:a,refresh:n,store:r},o))).then((function(){u.onSelect(lt({event:e,item:i,itemInputValue:a,itemUrl:l,refresh:n,source:u,state:r.getState()},o))}))}},a)}}}function pt(e){return pt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},pt(e)}function mt(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 vt(e){for(var t=1;t=5&&((o||!e&&5===r)&&(u.push(r,0,o,n),r=6),e&&(u.push(r,e,0,n),r=6)),o=""},l=0;l"===t?(r=1,o=""):o=t+o[0]:i?t===i?i="":o+=t:'"'===t||"'"===t?i=t:">"===t?(a(),r=1):r&&("="===t?(r=5,n=o,o=""):"/"===t&&(r<5||">"===e[l][c+1])?(a(),3===r&&(u=u[0]),r=u,(u=u[0]).push(2,0,r),r=0):" "===t||"\t"===t||"\n"===t||"\r"===t?(a(),r=2):o+=t),3===r&&"!--"===o&&(r=4,u=u[0])}return a(),u}(e)),t),arguments,[])).length>1?t:t[0]}var kt=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 xt(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 Nt(){for(var e=arguments.length,t=new Array(e),n=0;n2&&(u.children=arguments.length>3?Jt.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 sn(e,u,r,o,null)}function sn(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?++Yt:o};return null==o&&null!=Xt.vnode&&Xt.vnode(i),i}function fn(e){return e.children}function pn(e,t){this.props=e,this.context=t}function mn(e,t){if(null==t)return e.__?mn(e.__,e.__.__k.indexOf(e)+1):null;for(var n;tt&&Zt.sort(nn));yn.__r=0}function bn(e,t,n,r,o,i,u,a,l,c){var s,f,p,m,v,d,y,b=r&&r.__k||on,g=b.length;for(n.__k=[],s=0;s0?sn(m.type,m.props,m.key,m.ref?m.ref:null,m.__v):m)){if(m.__=n,m.__b=n.__b+1,null===(p=b[s])||p&&m.key==p.key&&m.type===p.type)b[s]=void 0;else for(f=0;f=0;t--)if((n=e.__k[t])&&(r=On(n)))return r;return null}function _n(e,t,n){"-"===t[0]?e.setProperty(t,null==n?"":n):e[t]=null==n?"":"number"!=typeof n||un.test(t)?n:n+"px"}function Sn(e,t,n,r,o){var i;e:if("style"===t)if("string"==typeof n)e.style.cssText=n;else{if("string"==typeof r&&(e.style.cssText=r=""),r)for(t in r)n&&t in n||_n(e.style,t,"");if(n)for(t in n)r&&n[t]===r[t]||_n(e.style,t,n[t])}else if("o"===t[0]&&"n"===t[1])i=t!==(t=t.replace(/Capture$/,"")),t=t.toLowerCase()in e?t.toLowerCase().slice(2):t.slice(2),e.l||(e.l={}),e.l[t+i]=n,n?r||e.addEventListener(t,i?Pn:jn,i):e.removeEventListener(t,i?Pn:jn,i);else if("dangerouslySetInnerHTML"!==t){if(o)t=t.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if("width"!==t&&"height"!==t&&"href"!==t&&"list"!==t&&"form"!==t&&"tabIndex"!==t&&"download"!==t&&t in e)try{e[t]=null==n?"":n;break e}catch(e){}"function"==typeof n||(null==n||!1===n&&"-"!==t[4]?e.removeAttribute(t):e.setAttribute(t,n))}}function jn(e){return this.l[e.type+!1](Xt.event?Xt.event(e):e)}function Pn(e){return this.l[e.type+!0](Xt.event?Xt.event(e):e)}function wn(e,t,n,r,o,i,u,a,l){var c,s,f,p,m,v,d,y,b,g,h,O,_,S,j,P=t.type;if(void 0!==t.constructor)return null;null!=n.__h&&(l=n.__h,a=t.__e=n.__e,t.__h=null,i=[a]),(c=Xt.__b)&&c(t);try{e:if("function"==typeof P){if(y=t.props,b=(c=P.contextType)&&r[c.__c],g=c?b?b.props.value:c.__:r,n.__c?d=(s=t.__c=n.__c).__=s.__E:("prototype"in P&&P.prototype.render?t.__c=s=new P(y,g):(t.__c=s=new pn(y,g),s.constructor=P,s.render=Cn),b&&b.sub(s),s.props=y,s.state||(s.state={}),s.context=g,s.__n=r,f=s.__d=!0,s.__h=[],s._sb=[]),null==s.__s&&(s.__s=s.state),null!=P.getDerivedStateFromProps&&(s.__s==s.state&&(s.__s=an({},s.__s)),an(s.__s,P.getDerivedStateFromProps(y,s.__s))),p=s.props,m=s.state,s.__v=t,f)null==P.getDerivedStateFromProps&&null!=s.componentWillMount&&s.componentWillMount(),null!=s.componentDidMount&&s.__h.push(s.componentDidMount);else{if(null==P.getDerivedStateFromProps&&y!==p&&null!=s.componentWillReceiveProps&&s.componentWillReceiveProps(y,g),!s.__e&&null!=s.shouldComponentUpdate&&!1===s.shouldComponentUpdate(y,s.__s,g)||t.__v===n.__v){for(t.__v!==n.__v&&(s.props=y,s.state=s.__s,s.__d=!1),s.__e=!1,t.__e=n.__e,t.__k=n.__k,t.__k.forEach((function(e){e&&(e.__=t)})),h=0;h0&&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(xn);r.add({value:t[0],isHighlighted:!0}),""!==t[1]&&r.add({value:t[1],isHighlighted:!1})})),r.get()}function Tn(e){return function(e){if(Array.isArray(e))return qn(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 qn(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 qn(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 qn(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n",""":'"',"'":"'"},Fn=new RegExp(/\w/i),Ln=/&(amp|quot|lt|gt|#39);/g,Un=RegExp(Ln.source);function Mn(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 Fn.test((o=i.value)&&Un.test(o)?o.replace(Ln,(function(e){return Rn[e]})):o)||a!==u?i.isHighlighted:a}function Hn(e){return Hn="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Hn(e)}function Vn(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 Wn(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 ur(e){return function(e){if(Array.isArray(e))return ar(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 ar(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 ar(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 ar(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(y.current||O.value.renderer.renderNoResults);return!n&&r||n},__autocomplete_metadata:{userAgents:br,options:e}}))})),j=f(n({collections:[],completion:null,context:{},isOpen:!1,query:"",activeItemId:null,status:"idle"},O.value.core.initialState)),P={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},w={setActiveItemId:S.value.setActiveItemId,setQuery:S.value.setQuery,setCollections:S.value.setCollections,setIsOpen:S.value.setIsOpen,setStatus:S.value.setStatus,setContext:S.value.setContext,refresh:S.value.refresh,navigator:S.value.navigator},I=m((function(){return Ct.bind(O.value.renderer.renderer.createElement)})),A=m((function(){return Gt({autocomplete:S.value,autocompleteScopeApi:w,classNames:O.value.renderer.classNames,environment:O.value.core.environment,isDetached:_.value,placeholder:O.value.core.placeholder,propGetters:P,setIsModalOpen:k,state:j.current,translations:O.value.renderer.translations})}));function E(){Ht(A.value.panel,{style:_.value?{}:yr({panelPlacement:O.value.renderer.panelPlacement,container:A.value.root,form:A.value.form,environment:O.value.core.environment})})}function D(e){j.current=e;var t={autocomplete:S.value,autocompleteScopeApi:w,classNames:O.value.renderer.classNames,components:O.value.renderer.components,container:O.value.renderer.container,html:I.value,dom:A.value,panelContainer:_.value?A.value.detachedContainer:O.value.renderer.panelContainer,propGetters:P,state:j.current,renderer:O.value.renderer.renderer},r=!b(e)&&!y.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;Vt(o.root,i.getRootProps(n({state:u,props:t.getRootProps({})},r))),Vt(o.input,i.getInputProps(n({state:u,props:t.getInputProps({inputElement:o.input}),inputElement:o.input},r))),Ht(o.label,{hidden:"stalled"===u.status}),Ht(o.loadingIndicator,{hidden:"stalled"!==u.status}),Ht(o.clearButton,{hidden:!u.query}),Ht(o.detachedSearchButtonQuery,{textContent:u.query}),Ht(o.detachedSearchButtonPlaceholder,{hidden:Boolean(u.query)})}(t),function(e,t){var r=t.autocomplete,o=t.autocompleteScopeApi,u=t.classNames,a=t.html,l=t.dom,c=t.panelContainer,s=t.propGetters,f=t.state,p=t.components,m=t.renderer;if(f.isOpen){c.contains(l.panel)||"loading"===f.status||c.appendChild(l.panel),l.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 l=e.source,c=e.items;return m.createElement("section",{key:t,className:u.source,"data-autocomplete-source-id":l.sourceId},l.templates.header&&m.createElement("div",{className:u.sourceHeader},l.templates.header({components:p,createElement:m.createElement,Fragment:m.Fragment,items:c,source:l,state:f,html:a})),l.templates.noResults&&0===c.length?m.createElement("div",{className:u.sourceNoResults},l.templates.noResults({components:p,createElement:m.createElement,Fragment:m.Fragment,source:l,state:f,html:a})):m.createElement("ul",i({className:u.list},s.getListProps(n({state:f,props:r.getListProps({source:l})},o))),c.map((function(e){var t=r.getItemProps({item:e,source:l});return m.createElement("li",i({key:t.id,className:u.item},s.getItemProps(n({state:f,props:t},o))),l.templates.item({components:p,createElement:m.createElement,Fragment:m.Fragment,item:e,state:f,html:a}))}))),l.templates.footer&&m.createElement("div",{className:u.sourceFooter},l.templates.footer({components:p,createElement:m.createElement,Fragment:m.Fragment,items:c,source:l,state:f,html:a})))})),d=m.createElement(m.Fragment,null,m.createElement("div",{className:u.panelLayout},v),m.createElement("div",{className:"aa-GradientBottom"})),y=v.reduce((function(e,t){return e[t.props["data-autocomplete-source-id"]]=t,e}),{});e(n(n({children:d,state:f,sections:v,elements:y},m),{},{components:p,html:a},o),l.panel)}else c.contains(l.panel)&&c.removeChild(l.panel)}(r,t)}function C(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};l();var t=O.value.renderer,n=t.components,r=u(t,gr);g.current=qt(r,O.value.core,{components:Bt(n,(function(e){return!e.value.hasOwnProperty("__autocomplete_componentName")})),initialState:j.current},e),v(),c(),S.value.refresh().then((function(){D(j.current)}))}function k(e){requestAnimationFrame((function(){var t=O.value.core.environment.document.body.contains(A.value.detachedOverlay);e!==t&&(e?(O.value.core.environment.document.body.appendChild(A.value.detachedOverlay),O.value.core.environment.document.body.classList.add("aa-Detached"),A.value.input.focus()):(O.value.core.environment.document.body.removeChild(A.value.detachedOverlay),O.value.core.environment.document.body.classList.remove("aa-Detached")))}))}return a((function(){var e=S.value.getEnvironmentProps({formElement:A.value.form,panelElement:A.value.panel,inputElement:A.value.input});return Ht(O.value.core.environment,e),function(){Ht(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?A.value.detachedOverlay:A.value.panel;return _.value&&j.current.isOpen&&k(!0),D(j.current),function(){e.contains(t)&&e.removeChild(t)}})),a((function(){var e=O.value.renderer.container;return e.appendChild(A.value.root),function(){e.removeChild(A.value.root)}})),a((function(){var e=p((function(e){D(e.state)}),0);return h.current=function(t){var n=t.state,r=t.prevState;(_.value&&r.isOpen!==n.isOpen&&k(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(){h.current=void 0}})),a((function(){var e=p((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){A.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({},w),{},{update:C,destroy:function(){l()}})},e.getAlgoliaFacets=function(e){var t=hr({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=Or,Object.defineProperty(e,"__esModule",{value:!0})})); + diff --git a/docs/site_libs/quarto-search/fuse.min.js b/docs/site_libs/quarto-search/fuse.min.js new file mode 100644 index 0000000..adc2835 --- /dev/null +++ b/docs/site_libs/quarto-search/fuse.min.js @@ -0,0 +1,9 @@ +/** + * Fuse.js v6.6.2 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2022 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 R(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,l=void 0===h?I.ignoreLocation:h,f=r/e.length;if(l)return f;var d=Math.abs(a-o);return u?f+d/u:d?1:f}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,l=i.includeMatches,f=void 0===l?I.includeMatches:l,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:f,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,l=void 0===h?I.findAllMatches:h,f=r.minMatchCharLength,d=void 0===f?I.minMatchCharLength:f,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=R(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,F&&(K[q]|=(A[q+1]|A[q])<<1|1|A[q+1]),K[q]&$&&(C=R(t,{errors:F,currentLocation:B,expectedLocation:b,distance:a,ignoreLocation:p}))<=x){if(x=C,(L=B)<=b)break;z=Math.max(1,2*b-L)}}if(R(t,{errors:F+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:l}),p=y.isMatch,m=y.score,k=y.indices;p&&(g=!0),v+=m,p&&k&&(d=[].concat(f(d),f(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=l(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=l(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=l(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=l(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=l(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=l(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=l(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,l=void 0===h?I.distance:h,f=o.includeMatches,d=void 0===f?I.includeMatches:f,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:l,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=l(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,l=void 0===h?I.ignoreLocation:h,f=n.findAllMatches,d=void 0===f?I.findAllMatches:f,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:l,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||F(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 fe(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(le(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=le(e)),i(e)}(e,this.options),r=function e(n,r,i){if(!n.children){var o=n.keyId,c=n.searcher,a=t._findMatches({key:t._keyStore.get(o),value:t._myIndex.getValueForItemAtKeyId(r,o),searcher:c});return a&&a.length?[{idx:i,item:r,matches:a}]:[]}for(var s=[],u=0,h=n.children.length;u1&&void 0!==arguments[1]?arguments[1]:{},n=t.getFn,r=void 0===n?I.getFn:n,i=t.fieldNormWeight,o=void 0===i?I.fieldNormWeight:i,c=e.keys,a=e.records,s=new $({getFn:r,fieldNormWeight:o});return s.setKeys(c),s.setIndexRecords(a),s},ye.config=I,function(){ne.push.apply(ne,arguments)}(te),ye},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).Fuse=t(); \ No newline at end of file diff --git a/docs/site_libs/quarto-search/quarto-search.js b/docs/site_libs/quarto-search/quarto-search.js new file mode 100644 index 0000000..aefb6a5 --- /dev/null +++ b/docs/site_libs/quarto-search/quarto-search.js @@ -0,0 +1,1247 @@ +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 && mainEl) { + // perform any highlighting + highlight(escapeRegExp(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 && 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" : "(max-width: 991px)"; + + // If configured, include the analytics client to send insights + const plugins = configurePlugins(quartoSearchOptions); + + let lastState = null; + const { setIsOpen, setQuery, setCollections } = autocomplete({ + container: searchEl, + detachedMediaQuery: detachedMediaQuery, + defaultActiveItemId: 0, + panelContainer: "#quarto-search-results", + panelPlacement: quartoSearchOptions["panel-placement"], + debug: false, + openOnFocus: true, + plugins, + classNames: { + form: "d-flex", + }, + placeholder: language["search-text-placeholder"], + 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 }) { + // If this is a file URL, note that + + // 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 }) { + if (query === null || query === "") { + return []; + } + + 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 }) { + const hasQuery = lastState.query; + + return createElement( + "div", + { + class: `quarto-search-no-results${ + hasQuery ? "" : " no-query" + }`, + }, + 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, + quartoSearchOptions + ); + }, + }, + }, + ]; + }, + }); + + window.quartoOpenSearch = () => { + setIsOpen(false); + setIsOpen(true); + focusSearchInput(); + }; + + document.addEventListener("keyup", (event) => { + const { key } = event; + const kbds = quartoSearchOptions["keyboard-shortcut"]; + const focusedEl = document.activeElement; + + const isFormElFocused = [ + "input", + "select", + "textarea", + "button", + "option", + ].find((tag) => { + return focusedEl.tagName.toLowerCase() === tag; + }); + + if ( + kbds && + kbds.includes(key) && + !isFormElFocused && + !document.activeElement.isContentEditable + ) { + event.preventDefault(); + window.quartoOpenSearch(); + } + }); + + // 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"); + } + } + + function throttle(func, wait) { + let waiting = false; + return function () { + if (!waiting) { + func.apply(this, arguments); + waiting = true; + setTimeout(function () { + waiting = false; + }, wait); + } + }; + } + + // 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 = throttle(() => { + // Only do this if we're not detached + // Bug #7117 + // This will happen when the keyboard is shown on ios (resulting in a scroll) + // which then closed the search UI + if (!window.matchMedia(detachedMediaQuery).matches) { + setIsOpen(false); + } + }, 50); + + 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.flatMap((event) => { + // This API limits the number of items per event to 20 + const chunkSize = 20; + const itemChunks = []; + const eventItems = event.items; + for (let i = 0; i < eventItems.length; i += chunkSize) { + itemChunks.push(eventItems.slice(i, i + chunkSize)); + } + // Split the items into multiple events that can be sent + const events = itemChunks.map((items) => { + return { + ...event, + items, + }; + }); + return events; + }); + + for (const event of events) { + insights.viewedObjectIDs(event); + } + }, + }); + 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; +var shownWarning = false; +async function readSearchData() { + // Initialize the search index on demand + if (fuseIndex === undefined) { + if (window.location.protocol === "file:" && !shownWarning) { + window.alert( + "Search requires JavaScript features disabled when running in file://... URLs. In order to use search, please run this document in a web server." + ); + shownWarning = true; + return; + } + // 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, + quartoSearchOptions +) { + switch (item.type) { + case kItemTypeDoc: + return createDocumentCard( + createElement, + "file-richtext", + item.title, + item.section, + item.text, + item.href, + item.crumbs, + quartoSearchOptions + ); + 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, + crumbs, + quartoSearchOptions +) { + const iconEl = createElement("i", { + class: `bi bi-${icon} search-result-icon`, + }); + const titleEl = createElement("p", { class: "search-result-title" }, title); + const titleContents = [iconEl, titleEl]; + const showParent = quartoSearchOptions["show-item-context"]; + if (crumbs && showParent) { + let crumbsOut = undefined; + const crumbClz = ["search-result-crumbs"]; + if (showParent === "root") { + crumbsOut = crumbs.length > 1 ? crumbs[0] : undefined; + } else if (showParent === "parent") { + crumbsOut = crumbs.length > 1 ? crumbs[crumbs.length - 2] : undefined; + } else { + crumbsOut = crumbs.length > 1 ? crumbs.join(" > ") : undefined; + crumbClz.push("search-result-crumbs-wrap"); + } + + const crumbEl = createElement( + "p", + { class: crumbClz.join(" ") }, + crumbsOut + ); + titleContents.push(crumbEl); + } + + const titleContainerEl = createElement( + "div", + { class: "search-result-title-container" }, + titleContents + ); + + 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); + } + } + } +} + +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string +} + +// 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", "crumbs"].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), + crumbs: result.item.crumbs, + }; + }); +} diff --git a/index.qmd b/index.qmd new file mode 100644 index 0000000..51c2811 --- /dev/null +++ b/index.qmd @@ -0,0 +1,214 @@ +# Cary Introduction to Python & Anaconda + + [![Nbviewer](https://img.shields.io/badge/render-nbviewer-lightgrey?logo=jupyter)](https://nbviewer.org/github/gregorywaynepower/cary-python-introduction/tree/main/) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/gregorywaynepower/cary-python-introduction/blob/main) + +## Navigating the File Tree in this Repository + +- For an Introduction to Data Analysis and a Refresher on Statistics + - [introduction_to_data_analysis.ipynb](/notebooks/introduction_to_data_analysis.ipynb) +- For an Introduction to Hypothesis Testing + - [hypothesis_testing.ipynb](/notebooks/hypothesis_testing.ipynb) +- For an Introduction to Python + - [python_101.ipynb](/notebooks/python_101.ipynb) +- Handling Common Errors in Python + - [python_errors.ipynb](/notebooks/python_errors.ipynb) +- Reading Local Files with `pandas` and visualizing datasets with `matplotlib` + - [reading_local_files.ipynb](/notebooks/reading_local_files.ipynb) +- Making DataFrames from API requests + - [making_dataframes_from_api_requests.ipynb](/notebooks/making_dataframes_from_api_requests.ipynb) +- What is wide vs long format data? + - [wide_vs_long.ipynb](/notebooks/wide_vs_long.ipynb) +- Cleaning data in `pandas` + - [cleaning_data.ipynb](/notebooks/cleaning_data.ipynb) +- Handling Data Issues in `pandas` + - [handling_data_issues.ipynb](/notebooks/handling_data_issues.ipynb) +- Introduction to [`plotly.express`](https://plotly.com/python/plotly-express/) + - [intro_to_plotly_express.ipynb](/notebooks/intro_to_plotly_express.ipynb) +- Introduction to `matplotlib` + - [introducing_matplotlib.ipynb](/notebooks/introducing_matplotlib.ipynb) +- Plotting with `plot()` method for `pandas` objects + - [plotting_with_pandas.ipynb](/notebooks/plotting_with_pandas.ipynb) +- Introduction to `pandas.plotting()` module + - [pandas_plotting_module.ipynb](/notebooks/pandas_plotting_module.ipynb) + +## Installing This Repository's Depencies using `conda-lock.yml` or `environment.yml` Files + +You can install project dependencies either using out-of-the-box conda CLI commands, or installing conda-lock to ensure dependencies are solved no matter the platform you are on. + +```bash +conda install -c conda-forge conda-lock +conda-lock install --name name-of-your-environment conda-lock.yml +conda activate name-of-your-environment +``` + +```bash +conda env create -n name-of-your-environment --file environment.yml +conda activate name-of-your-environment +``` + +## Missing an Imported Module? + +1. Install your module using `conda install name-of-module` [in your terminal or Anaconda Prompt](https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-pkgs.html#id2) +2. [Install your module with Anaconda Navigator](https://docs.anaconda.com/navigator/getting-started/#managing-packages) + 1. Open Anaconda Navigator + 2. Click Environments tab + 3. Select the Environment you want to install a module into + +Please **don't use** `python -m pip install name-of-module` when installing packages without activating your conda environment via `conda activate name-of-environment` first. + +[Anaconda's Explanation of conda & pip if you want a more in-depth explanation.](https://www.anaconda.com/blog/understanding-conda-and-pip "https://www.anaconda.com/blog/understanding-conda-and-pip") + +## Anaconda's Free [with Account Creation] Courses + +- [Getting Started with Anaconda](https://freelearning.anaconda.cloud/get-started-with-anaconda) +- [conda Basics](https://freelearning.anaconda.cloud/conda-basics) +- [Jupyter Notebook Basics](https://freelearning.anaconda.cloud/jupyter-notebook-basics) + +## Anaconda Ecosystem + +- [Anaconda Ecosystem Documentation](https://docs.anaconda.com/) + +### Anaconda Navigator (GUI) + +- [Anaconda Navigator: Getting Started](https://docs.anaconda.com/navigator/getting-started/) +- [Anaconda Navigator Documentation](https://docs.anaconda.com/navigator/) + +#### Trying to Explore Packages Not Installed? + +If your list of "Not Installed" packages is blank, I recommend [manually updating](https://docs.anaconda.com/navigator/update-navigator/#manual-update) Anaconda Navigator. + +### conda (CLI) + +- [conda: Getting Started](https://conda.io/projects/conda/en/latest/user-guide/getting-started.html) +- [conda Documenation](https://docs.conda.io/projects/conda/en/stable/) +- [conda Cheat Sheet](https://docs.conda.io/projects/conda/en/latest/user-guide/cheatsheet.html) + +#### Need to Rollback to a Previous Version of a conda? + +If you are experimenting with your `conda base` environment and need to restore a previous version of a conda. + +1. You can use the `conda list` command with the `--revisions` flag to view your conda revision history. +2. You can use the `conda install` command with the `--revision` flag with the number that corresponds to the version you want to rollback to. + +```bash or Powershell +conda list --revisions +conda install --revision N +# Replace N with the number that corresponds to the version you want to rollback to. +``` + +#### Trying to make conda Faster? + +- [Anaconda: Conda is Fast Now. Like, Really Fast](https://www.anaconda.com/blog/conda-is-fast-now) +- [conda-libmamba-solver: Getting Started](https://conda.github.io/conda-libmamba-solver/getting-started/) +- [Replace it with `mamba`; even for existing `conda` installations](https://mamba.readthedocs.io/en/latest/installation.html) + +## Python 3 + +### Official Documentation + +1. [Python.org's 'Whetting Your Appetite'](https://docs.python.org/3/tutorial/appetite.html) +2. [Python.org's Official Python 3 Tutorial](https://docs.python.org/3/tutorial/ "https://docs.python.org/3/tutorial/") +3. [Python.org's Glossary of Terms](https://docs.python.org/3/glossary.html#glossary) +4. [Python.org's Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/) + +## Python Modules + +### `polars` + +For in-memory analysis of tabular data and introduces the DataFrame for larger-than-memory datasets and want an API similar to pandas. + +- [Landing Page for `polars`](https://pola-rs.github.io/polars-book/) + +### `pandas` + +For in-memory analysis of tabular data and introduces the DataFrame + +- [Landing page for `pandas`](https://pandas.pydata.org/docs/getting_started/index.html) +- [pandas Compared to Spreadsheets](https://pandas.pydata.org/docs/getting_started/comparison/comparison_with_spreadsheets.html) +- [pandas Compared to SQL Queries](https://pandas.pydata.org/docs/getting_started/comparison/comparison_with_sql.html) +- [pandas Compared to SAS](https://pandas.pydata.org/docs/getting_started/comparison/comparison_with_sas.html) + +### `geopandas` + +If you need your DataFrame to be mapped or do geospatial calculations. + +- [Landing page for `geopandas`](https://geopandas.org/en/stable/) +- [`geopandas` User Guide](https://geopandas.org/en/stable/docs/user_guide.html) + +### `folium` + +If you need more interactivity from your generated map. + +- [Landing page for `folium`](https://github.com/python-visualization/folium) + +### `cartopy` + +If you need more control over your generated static map projection. + +- [Landing page for `cartopy`](https://scitools.org.uk/cartopy/docs/latest/) + +### `xarray` + +If you need your dataset to have more than two dimensions. + +- [Landing Page for `xarray`](https://docs.xarray.dev/en/stable/) + +### `matplotlib` + +Highly configurable visualization library that other libraries build off of. + +- [Landing page for `matplotlib`](https://matplotlib.org/) + +### `seaborn` + +High-level library for generating statistical graphics, especially for long data format. + +- [Landing page for `seaborn`](https://seaborn.pydata.org/) + +### `plotly.express` + +Generate interactive graphics, with a focus on exploratory analysis with visuals. + +- [Landing page for `plotly.express`](https://plotly.com/python/plotly-express/) + +## Recommended Books + +I highly recommend going through the official Python 3 tutorial first. It's a great way to get your feet wet and get a feel for the language. However, here are some books I recommend if you want to go deeper or explore certain topics. + +1. [Automate the Boring Stuff with Python by Al Sweigart](https://automatetheboringstuff.com/) +2. [Data Analysis with Pandas: 2nd Edition by Stefanie Molin](https://github.com/stefmolin/Hands-On-Data-Analysis-with-Pandas-2nd-edition) +3. [Data Science at the Command Line: 2nd Edition by Jeoroen Janssens](https://jeroenjanssens.com/dsatcl/) + +## Youtube Resources to Check Out + +1. [Rob Mulla on YouTube for Data Science with Focus on Python](https://www.youtube.com/@robmulla "https://www.youtube.com/@robmulla") + 1. [Playlist: Medallion Python Data Science Coding Videos](https://www.youtube.com/playlist?list=PL7RwtdVQXQ8qxBH6ugYn50D0M5u--2Xx4 "https://www.youtube.com/playlist?list=pl7rwtdvqxq8qxbh6ugyn50d0m5u--2xx4") + 2. [Playlist: Working with Data in Python](https://www.youtube.com/playlist?list=PL7RwtdVQXQ8oYpuIIDWR0SaaSCe8ZeZ7t "https://www.youtube.com/playlist?list=pl7rwtdvqxq8oypuiidwr0saasce8zez7t") +2. [Conference Talk: So you want to be a Python expert?](https://www.youtube.com/watch?v=cKPlPJyQrt4) +3. [Conference Talk: 1000x faster data manipulation: vectorizing with `pandas` and `numpy`](https://www.youtube.com/watch?v=nxWginnBklU "https://www.youtube.com/watch?v=nxWginnBklU") +4. [Conference Talk: No More Sad Pandas: Optimizing Pandas Code for Sped and Efficiency](https://www.youtube.com/watch?v=HN5d490_KKk) +5. [Conference Talk: Effective Pandas](https://www.youtube.com/watch?v=zgbUk90aQ6A) +6. [Conference Tutorial: So You Wanna Be a Pandas Expert?](https://www.youtube.com/watch?v=pjq3QOxl9Ok) + +## Topics on the Table + +- Installing [Anaconda's](https://www.anaconda.com/) Package & Environment Manager [`conda`](https://docs.conda.io/projects/conda/en/stable/) (Command Line Interface Tool) and [Anaconda-Navigator](https://docs.anaconda.com/navigator/index.html) (Graphical User Interface Tool) (Best practice when it comes to dependency management for Python and R) +- Picking an editor of your choice that supports JupyterNotebooks ([Visual Studio Code](https://code.visualstudio.com/) or [JupyterLab](https://jupyter.org/)) +- How do I get data into Python and get descriptive statistics? (reading files with [pandas](https://pandas.pydata.org/docs/getting_started/index.html#getting-started)) +- Now paint me a picture with the data. (introduction to [Plotly](https://plotly.com/python/) & [Holoviz Ecosystem](https://holoviz.org/index.html)) +- How do I share this? + - [Binder](https://mybinder.org/) if you want interactivity (a little more setup) + - [nbviewer](https://nbviewer.org/) if you value sharing your rendered files (less setup but not as pretty) + +## Sidequests + +- How to work with the Terminal + - [Using Powershell](https://learn.microsoft.com/en-us/powershell/scripting/overview) +- Basics of version control using Git & pushing to Github + - [Official Git Documentation & Cheatsheets](https://git-scm.com/doc) + - [Git & Github Crash Course for Beginners](https://www.youtube.com/watch?v=HVsySz-h9r4&list=PL-osiE80TeTuRUfjRe54Eea17-YfnOOAx&index=1) + - [Want Richer Change History for Notebooks? Try ReviewNB](https://www.reviewnb.com) + +## Contributing + +If you have anything you want to cover, I'm open to suggestions. Feel free to checkout the [contributing guidelines](CONTRIBUTING.md) for ways to offer feedback and contribute. My previous experience with python covers web scraping, cleaning data, statistical analysis, and moving data into and out of databases. diff --git a/scripts/cleaning_data.py b/scripts/cleaning_data.py deleted file mode 100644 index da539d5..0000000 --- a/scripts/cleaning_data.py +++ /dev/null @@ -1,424 +0,0 @@ -# %% [markdown] # Cleaning Data - -# ## About the data -# In this notebook, we will using daily temperature data from the [National Centers for Environmental Information (NCEI) -# API](https://www.ncdc.noaa.gov/cdo-web/webservices/v2). We will use the -# Global Historical Climatology Network - Daily (GHCND) dataset; see the -# documentation -# [here](https://www1.ncdc.noaa.gov/pub/data/cdo/documentation/GHCND_documentation.pdf). -# -# This data was collected from the LaGuardia Airport station in New York City -# for October 2018. It contains: - the daily minimum temperature (`TMIN`) - the -# daily maximum temperature (`TMAX`) - the daily average temperature (`TAVG`) -# -# *Note: The NCEI is part of the National Oceanic and Atmospheric -# Administration (NOAA) and, as you can see from the URL for the API, this -# resource was created when the NCEI was called the NCDC. Should the URL for -# this resource change in the future, you can search for "NCEI weather API" to -# find the updated one.* -# -# In addition, we will be using S&P 500 stock market data (obtained using the -# [`stock_analysis`](https://github.com/stefmolin/stock-analysis) package we -# will build in chapter 7) and data for bitcoin for 2017 through 2018. For the -# first edition, the bitcoin data was collected from CoinMarketCap using the -# `stock_analysis` package; however, changes in the website led to the -# necessity of changing the data source to Yahoo! Finanbe. The bitcoin data -# that was collected before the CoinMarketCap website change should be -# equivalent to the historical data that can be viewed on -# [this](https://coinmarketcap.com/currencies/bitcoin/historical-data/) page. -# -# ## Setup We need to import `pandas` and read in the temperature data to get -# started: - -# %% -import pandas as pd - -df = pd.read_csv("../data/nyc_temperatures.csv") - -df.head() - -# %% [markdown] ## Renaming Columns We start out with the following columns: - -# %% -df.columns - -# %% [markdown] We want to rename the `value` column to indicate it contains -# the temperature in Celsius and the `attributes` column to say `flags` since -# each value in the comma-delimited string is a different flag about the data -# collection. For this task, we use the `rename()` method and pass in a -# dictionary mapping the column names to their new names. We pass -# `inplace=True` to change our original dataframe instead of getting a new one -# back: - -# %% -df.rename(columns={"value": "temp_C", "attributes": "flags"}, inplace=True) - -# %% [markdown] Those columns have been successfully renamed: - -# %% -df.columns - -# %% [markdown] We can also perform string operations on the column names with -# `rename()`: - -# %% -df.rename(str.upper, axis="columns").columns - -# %% [markdown] ## Type Conversion The `date` column is not currently being -# stored as a `datetime`: - -# %% -df.dtypes - -# %% [markdown] Let's perform the conversion with `pd.to_datetime()`: - -# %% -df.loc[:, "date"] = pd.to_datetime(df.date) -df.dtypes - -# %% [markdown] Now we get useful information when we use `describe()` on this -# column: - -# %% -df.date.describe() - -# %% [markdown] We can use `tz_localize()` on a `DatetimeIndex` object to -# convert to a desired timezone: - -# %% -pd.date_range(start="2018-10-25", periods=2, freq="D").tz_localize("EST") - -# %% [markdown] This also works with `Series`/`DataFrame` objects that have an -# index of type `DatetimeIndex`. Let's read in the CSV again for this example -# and set the `date` column to be the index and stored as a datetime: - -# %% -eastern = pd.read_csv( - "../data/nyc_temperatures.csv", index_col="date", parse_dates=True -).tz_localize("EST") -eastern.head() - -# %% [markdown] We can use `tz_convert()` to convert to another timezone from -# there. If we convert the Eastern datetimes to UTC, they will now be at 5 AM, -# since `pandas` will use the offsets to convert: - -# %% -eastern.tz_convert("UTC").head() - -# %% [markdown] We can change the period of the index as well. We could change -# the period to be monthly to make it easier to aggregate later. -# -# The reason we have to add the parameter within `tz_localize()` to `None` for -# this, is because we'll get a warning from `pandas` that our output class -# `PeriodArray` doesn't have time zone information and we'll lose it. - -# %% -eastern.tz_localize(None).to_period("M").index - -# %% [markdown] We now get a `PeriodIndex` object, which we can change back -# into a `DatetimeIndex` object with `to_timestamp()`: - -# %% -eastern.tz_localize(None).to_period("M").to_timestamp().index - -# %% [markdown] We can use the `assign()` method for working with multiple -# columns at once (or creating new ones). Since our `date` column has already -# been converted, we need to read in the data again: - -# %% -df = pd.read_csv("../data/nyc_temperatures.csv").rename( - columns={"value": "temp_C", "attributes": "flags"} -) - -new_df = df.assign(date=pd.to_datetime(df.date), temp_F=(df.temp_C * 9 / 5) + 32) -new_df.dtypes - -# %% [markdown] The `date` column now has datetimes and the `temp_F` column was -# added: - -# %% -new_df.head() - -# %% [markdown] We can also use `astype()` to perform conversions. Let's create -# columns of the integer portion of the temperatures in Celsius and Fahrenheit. -# We will use **lambda functions** (first introduced in *Chapter 2, Working -# with Pandas DataFrames*), so that we can use the values being created in the -# `temp_F` column to calculate the `temp_F_whole` column. It is very common -# (and useful) to use lambda functions with `assign()`: - -# %% -df = df.assign( - date=lambda x: pd.to_datetime(x.date), - temp_C_whole=lambda x: x.temp_C.astype("int"), - temp_F=lambda x: (x.temp_C * 9 / 5) + 32, - temp_F_whole=lambda x: x.temp_F.astype("int"), -) - -df.head() - -# %% [markdown] Creating categories: - -# %% -df_with_categories = df.assign( - station=df.station.astype("category"), datatype=df.datatype.astype("category") -) -df_with_categories.dtypes - -# %% -df_with_categories.describe(include="category") - -# %% [markdown] Our categories have no order, but this is something that -# `pandas` supports: - -# %% -pd.Categorical( - ["med", "med", "low", "high"], categories=["low", "med", "high"], ordered=True -) - -# %% [markdown] ## Reordering, reindexing, and sorting Say we want to find the -# days that reached the hottest temperatures in the weather data; we can sort -# our values by the `temp_C` column with the largest on top to find this: - -# %% -df[df.datatype == "TMAX"].sort_values(by="temp_C", ascending=False).head(10) - -# %% [markdown] However, this isn't perfect because we have some ties, and they -# aren't sorted consistently. In the first tie between the 7th and the 10th, -# the earlier date comes first, but the opposite is true with the tie between -# the 4th and the 2nd. We can use other columns to break ties and specify how -# to sort each with `ascending`. Let's break ties with the date column and show -# earlier dates before later ones: - -# %% -df[df.datatype == "TMAX"].sort_values( - by=["temp_C", "date"], ascending=[False, True] -).head(10) - -# %% [markdown] Notice that the index was jumbled in the past 2 results. Here, -# our index only stores the row number in the original data, but we may not -# need to keep track of that information. In this case, we can pass in -# `ignore_index=True` to get a new index after sorting: - -# %% -df[df.datatype == "TMAX"].sort_values( - by=["temp_C", "date"], ascending=[False, True], ignore_index=True -).head(10) - -# %% [markdown] When just looking for the n-largest values, rather than wanting -# to sort all the data, we can use `nlargest()`: - -# %% -df[df.datatype == "TAVG"].nlargest(n=10, columns="temp_C") - -# %% [markdown] We use `nsmallest()` for the n-smallest values. - -# %% -df.nsmallest(n=5, columns=["temp_C", "date"]) - -# %% [markdown] The `sample()` method will give us rows (or columns with -# `axis=1`) at random. We can provide a seed (`random_state`) to make this -# reproducible. The index after we do this is jumbled: - -# %% -df.sample(5, random_state=0).index - -# %% [markdown] We can use `sort_index()` to order it again: - -# %% -df.sample(5, random_state=0).sort_index().index - -# %% [markdown] The `sort_index()` method can also sort columns alphabetically: - -# %% -df.sort_index(axis=1).head() - -# %% [markdown] This can make selection with `loc` easier for many columns: - -# %% -df.sort_index(axis=1).head().loc[:, "temp_C":"temp_F_whole"] - -# %% [markdown] We must sort the index to compare two dataframes. If the index -# is different, but the data is the same, they will be marked not-equal: - -# %% -df.equals(df.sort_values(by="temp_C")) - -# %% [markdown] Sorting the index solves this issue: - -# %% -df.equals(df.sort_values(by="temp_C").sort_index()) - -# %% [markdown] Let's set the `date` column as our index: - -# %% -df.set_index("date", inplace=True) -df.head() - -# %% [markdown] Now that we have an index of type `DatetimeIndex`, we can do -# datetime slicing and indexing. As long as we provide a date format that -# pandas understands, we can grab the data. To select all of 2018, we simply -# use `df.loc['2018']`, for the fourth quarter of 2018 we can use -# `df.loc['2018-Q4']`, grabbing October is as simple as using -# `df.loc['2018-10']`; these can also be combined to build ranges. Let's grab -# October 11, 2018 through October 12, 2018 (inclusive of both -# endpoints)—note that using `loc[]` is optional for ranges: - -# %% -df["2018-10-11":"2018-10-12"] - -# %% [markdown] We can also use `reset_index()` to get a fresh index and move -# our current index into a column for safe keeping. This is especially useful -# if we had data, such as the date, in the index that we don't want to lose: - -# %% -df["2018-10-11":"2018-10-12"].reset_index() - -# %% [markdown] Reindexing allows us to conform our axis to contain a given set -# of labels. Let's turn to the S&P 500 stock data in the `sp500.csv` file to -# see an example of this. Notice we only have data for trading days (weekdays, -# excluding holidays): - -# %% -sp = pd.read_csv("../data/sp500.csv", index_col="date", parse_dates=True).drop( - columns=["adj_close"] -) - -sp.head(10).assign(day_of_week=lambda x: x.index.day_name()) - -# %% [markdown] If we want to look at the value of a portfolio (group of -# assets) that trade on different days, we need to handle the mismatch in the -# index. Bitcoin, for example, trades daily. If we sum up all the data we have -# for each day (aggregations will be covered in chapter 4, so don't fixate on -# this part), we get the following: - -# %% -bitcoin = pd.read_csv("../data/bitcoin.csv", index_col="date", parse_dates=True).drop( - columns=["market_cap"] -) - -# every day's closing price = S&P 500 close + Bitcoin close (same for other -# metrics) -portfolio = pd.concat([sp, bitcoin], sort=False).groupby(level="date").sum() - -portfolio.head(10).assign(day_of_week=lambda x: x.index.day_name()) - -# %% [markdown] It may not be immediately obvious what is wrong with the -# previous data, but with a visualization we can easily see the cyclical -# pattern of drops on the days the stock market is closed. (Don't worry about -# the plotting code too much, we will cover it in depth in chapters 5 and 6). -# -# We will need to import `matplotlib` now: - -# %% -import matplotlib.pyplot as plt # we use this module for plotting from matplotlib.ticker -from matplotlib.ticker import StrMethodFormatter # for formatting the axis - -# %% [markdown] Now we can see why we need to reindex: - -# %% plot the closing price from Q4 2017 through Q2 2018 -ax = portfolio["2017-Q4":"2018-Q2"].plot( - y="close", - figsize=(15, 5), - legend=False, - title="Bitcoin + S&P 500 value without accounting for different indices", -) - -# formatting -ax.set_ylabel("price") -ax.yaxis.set_major_formatter(StrMethodFormatter("${x:,.0f}")) - -for spine in ["top", "right"]: - ax.spines[spine].set_visible(False) - -# show the plot -plt.show() - -# %% [markdown] We need to align the index of the S&P 500 to match bitcoin in -# order to fix this. We will use the `reindex()` method, but by default we get -# `NaN` for the values that we don't have data for: - -# %% -sp.reindex(bitcoin.index).head(10).assign(day_of_week=lambda x: x.index.day_name()) - -# %% [markdown] So now we have rows for every day of the year, but all the -# weekends and holidays have `NaN` values. To address this, we can specify how -# to handle missing values with the `method` argument. In this case, we want to -# forward-fill, which will put the weekend and holiday values as the value they -# had for the Friday (or end of trading week) before: - -# %% -sp.reindex(bitcoin.index, method="ffill").head(10).assign( - day_of_week=lambda x: x.index.day_name() -) - -# %% [markdown] To isolate the changes happening with the forward-filling, we -# can use the `compare()` method. It shows us the values that differ across -# identically-labeled dataframes (same names and same columns). Here, we can -# see that only weekends and holidays (Monday, January 16, 2017 was MLK day) -# have values forward-filled. Notice that consecutive days have the same -# values. - -# %% -sp.reindex(bitcoin.index).compare(sp.reindex(bitcoin.index, method="ffill")).head( - 10 -).assign(day_of_week=lambda x: x.index.day_name()) - -# %% [markdown] This isn't perfect though. We probably want 0 for the volume -# traded and to put the closing price for the open, high, low, and close on the -# days the market is closed: -# -# The reason why we're using `np.where(boolean condition, value if True, value -# if False)` within `lambda` functions in the example below, is that -# vectorized operations allow us to be faster and more efficient than -# utilizing `for` loops to perform calculations on arrays all at once. - -# %% -import numpy as np - -sp_reindexed = sp.reindex(bitcoin.index).assign( - volume=lambda x: x.volume.fillna(0), - # put 0 when market is closed - close=lambda x: x.close.fillna(method="ffill"), - # carry this forward - # take the closing price if - # these aren't available - open=lambda x: np.where(x.open.isnull(), x.close, x.open), - high=lambda x: np.where(x.high.isnull(), x.close, x.high), - low=lambda x: np.where(x.low.isnull(), x.close, x.low), -) -sp_reindexed.head(10).assign(day_of_week=lambda x: x.index.day_name()) - -# %% [markdown] If we create a visualization comparing the reindexed data to -# the first attempt, we see how reindexing helped maintain the asset value when -# the market was closed: - -# %% every day's closing price = S&P 500 close adjusted for market closure + -# Bitcoin close (same for other metrics) -fixed_portfolio = sp_reindexed + bitcoin - -# plot the reindexed portfolio's closing price from Q4 2017 through Q2 2018 -ax = fixed_portfolio["2017-Q4":"2018-Q2"].plot( - y="close", - label="reindexed portfolio of S&P 500 + Bitcoin", - figsize=(15, 5), - linewidth=2, - title="Reindexed portfolio vs. portfolio with mismatched indices", -) - -# add line for original portfolio for comparison -portfolio["2017-Q4":"2018-Q2"].plot( - y="close", - ax=ax, - linestyle="--", - label="portfolio of S&P 500 + Bitcoin w/o reindexing", -) - -# formatting -ax.set_ylabel("price") -ax.yaxis.set_major_formatter(StrMethodFormatter("${x:,.0f}")) - -for spine in ["top", "right"]: - ax.spines[spine].set_visible(False) - -# show the plot -plt.show() diff --git a/scripts/handling_data_issues.py b/scripts/handling_data_issues.py deleted file mode 100644 index e4e5a0f..0000000 --- a/scripts/handling_data_issues.py +++ /dev/null @@ -1,366 +0,0 @@ -# %% [markdown] # Handling duplicate, missing, or invalid data -# -# ## About the data In this notebook, we will using daily weather data that was -# taken from the [National Centers for Environmental Information (NCEI) -# API](https://www.ncdc.noaa.gov/cdo-web/webservices/v2) and altered to -# introduce many common problems faced when working with data. -# -# *Note: The NCEI is part of the National Oceanic and Atmospheric -# Administration (NOAA) and, as you can see from the URL for the API, this -# resource was created when the NCEI was called the NCDC. Should the URL for -# this resource change in the future, you can search for "NCEI weather API" to -# find the updated one.* -# -# ## Background on the data -# -# Data meanings: -# - `PRCP`: precipitation in millimeters -# - `SNOW`: snowfall in millimeters -# - `SNWD`: snow depth in millimeters -# - `TMAX`: maximum daily temperature in Celsius -# - `TMIN`: minimum daily temperature in Celsius -# - `TOBS`: temperature at time of observation in Celsius -# - `WESF`: water equivalent of snow in millimeters -# -# Some important facts to get our bearings: -# - According to the National Weather Service, the coldest temperature ever -# recorded in Central Park was -15°F (-26.1°C) on February 9, 1934: -# [source](https://www.weather.gov/media/okx/Climate/CentralPark/extremes.pdf) -# - The temperature of the Sun's photosphere is approximately 5,505°C: [source](https://en.wikipedia.org/wiki/Sun) -# -# ## Setup -# We need to import `pandas` and read in the dirty data to get started: - -# %% -import pandas as pd - -df = pd.read_csv('../data/dirty_data.csv') - -# %% [markdown] -# ## Finding problematic data -# A good first step is to look at some rows: - -# %% -df.head() - -# %% [markdown] -# Looking at summary statistics can reveal strange or missing values: - -# %% -df.describe() - -# %% [markdown] -# The `info()` method can pinpoint missing values and wrong data types: - -# %% -df.info() - -# %% [markdown] -# We can use the `isna()`/`isnull()` method of the series to find nulls: - -# %% -contain_nulls = df[ - df.SNOW.isna() | df.SNWD.isna() | df.TOBS.isna() - | df.WESF.isna() | df.inclement_weather.isna() -] -contain_nulls.shape[0] - -# %% -contain_nulls.head(10) - -# %% [markdown] -# Note that we can't check if we have `NaN` like this: - -# %% -df[df.inclement_weather == 'NaN'].shape[0] - -# %% [markdown] -# This is because it is actually `np.nan`. However, notice this also doesn't work: - -# %% -import numpy as np -df[df.inclement_weather == np.nan].shape[0] - -# %% [markdown] -# We have to use one of the methods discussed earlier for this to work: - -# %% -df[df.inclement_weather.isna()].shape[0] - -# %% [markdown] -# We can find `-inf`/`inf` by comparing to `-np.inf`/`np.inf`: - -# %% -df[df.SNWD.isin([-np.inf, np.inf])].shape[0] - -# %% [markdown] -# Rather than do this for each column, we can write a function that will use a -# [dictionary comprehension](https://www.python.org/dev/peps/pep-0274/) to -# check all the columns for us: - -# %% -def get_inf_count(df): - """Find the number of inf/-inf values per column in the dataframe""" - return { - col: df[df[col].isin([np.inf, -np.inf])].shape[0] for col in df.columns - } - -get_inf_count(df) - -# %% [markdown] -# Before we can decide how to handle the infinite values of snow depth, we -# should look at the summary statistics for snowfall, which forms a big part in -# determining the snow depth: - -# %% -pd.DataFrame({ - 'np.inf Snow Depth': df[df.SNWD == np.inf].SNOW.describe(), - '-np.inf Snow Depth': df[df.SNWD == -np.inf].SNOW.describe() -}).T - -# %% [markdown] -# Let's now look into the `date` and `station` columns. We saw the `?` for -# station earlier, so we know that was the other unique value. However, we see -# that some dates are present 8 times in the data and we only have 324 days -# meaning we are also missing days: - -# %% -df.describe(include='object') - -# %% [markdown] -# We can use the `duplicated()` method to find duplicate rows: - -# %% -df[df.duplicated()].shape[0] - -# %% [markdown] -# The default for `keep` is `'first'` meaning it won't show the first row that -# the duplicated data was seen in; we can pass in `False` to see it though: - -# %% -df[df.duplicated(keep=False)].shape[0] - -# %% [markdown] -# We can also specify the columns to use: - -# %% -df[df.duplicated(['date', 'station'])].shape[0] - -# %% [markdown] -# Let's look at a few duplicates. Just in the few values we see here, we know -# that the top 4 are actually in the data 6 times because by default we aren't -# seeing their first occurrence: - -# %% -df[df.duplicated()].head() - -# %% [markdown] -# ## Mitigating Issues -# -# ### Handling duplicated data -# Since we know we have NY weather data and noticed we only had two entries for -# `station`, we may decide to drop the `station` column because we are only -# interested in the weather data. However, when dealing with duplicate data, we -# need to think of the ramifications of removing it. Notice we only have data -# for the `WESF` column when the station is `?`: - -# %% -df[df.WESF.notna()].station.unique() - -# %% [markdown] -# If we determine it won't impact our analysis, we can use `drop_duplicates()` -# to remove them: - -# %% -# 1. make the date a datetime -df.date = pd.to_datetime(df.date) - -# 2. save this information for later -station_qm_wesf = df[df.station == '?'].drop_duplicates('date').set_index('date').WESF - -# 3. sort ? to the bottom -df.sort_values('station', ascending=False, inplace=True) - -# 4. drop duplicates based on the date column keeping the first occurrence -# which will be the valid station if it has data -df_deduped = df.drop_duplicates('date') - -# 5. remove the station column because we are done with it -df_deduped = df_deduped.drop(columns='station').set_index('date').sort_index() - -# 6. take valid station's WESF and fall back on station ? if it is null -df_deduped = df_deduped.assign( - WESF=lambda x: x.WESF.combine_first(station_qm_wesf) -) - -df_deduped.shape - -# %% [markdown] -# Here we used the `combine_first()` method to coalesce the values to the first -# non-null entry; this means that if we had data from both stations, we would -# first take the value provided by the named station and if (and only if) that -# station was null would we take the value from the station named `?`. The -# following table contains some examples of how this would play out: -# -# | station GHCND:USC00280907 | station ? | result of `combine_first()` | -# | :---: | :---: | :---: | -# | 1 | 17 | 1 | -# | 1 | `NaN` | 1 | -# | `NaN` | 17 | 17 | -# | `NaN` | `NaN` | `NaN` | -# -# Check out the 4th row—we have `WESF` in the correct spot thanks to the index: - -# %% -df_deduped.head() - -# %% [markdown] -# ### Dealing with nulls -# We could drop nulls, replace them with some arbitrary value, or impute them -# using the surrounding data. Each of these options may have ramifications, so -# we must choose wisely. -# -# We can use `dropna()` to drop rows where any column has a null value. The -# default options leave us hardly any data: - -# %% -df_deduped.dropna().shape - -# %% [markdown] -# If we pass `how='all'`, we can choose to only drop rows where everything is -# null, but this removes nothing: - -# %% -df_deduped.dropna(how='all').shape - -# %% [markdown] -# We can use just a subset of columns to determine what to drop with the -# `subset` argument: - -# %% -df_deduped.dropna( - how='all', subset=['inclement_weather', 'SNOW', 'SNWD'] -).shape - -# %% [markdown] -# This can also be performed along columns, and we can also require a certain -# number of null values before we drop the data: - -# %% -df_deduped.dropna(axis='columns', thresh=df_deduped.shape[0] * .75).columns - -# %% [markdown] -# We can choose to fill in the null values instead with `fillna()`: - -# %% -df_deduped.loc[:,'WESF'].fillna(0, inplace=True) -df_deduped.head() - -# %% [markdown] -# At this point we have done everything we can without distorting the data. We -# know that we are missing dates, but if we reindex, we don't know how to fill -# in the `NaN` data. With the weather data, we can't assume because it snowed -# one day that it will snow the next or that the temperature will be the same. -# For this reason, note that the next few examples are just for illustrative -# purposes only—just because we can do something doesn't mean we should. -# -# That being said, let's try to address some of remaining issues with the -# temperature data. We know that when `TMAX` is the temperature of the Sun, it -# must be because there was no measured value, so let's replace it with `NaN`. -# We will also do so for `TMIN` which currently uses -40°C for its placeholder -# when we know that the coldest temperature ever recorded in NYC was -15°F -# (-26.1°C) on February 9, 1934: - -# %% -df_deduped = df_deduped.assign( - TMAX=lambda x: x.TMAX.replace(5505, np.nan), - TMIN=lambda x: x.TMIN.replace(-40, np.nan), -) - -# %% [markdown] -# We will also make an assumption that the temperature won't change drastically -# day-to-day. Note that this is actually a big assumption, but it will allow us -# to understand how `fillna()` works when we provide a strategy through the -# `method` parameter. The `fillna()` method gives us 2 options for the `method` -# parameter: -# - `'ffill'` to forward-fill -# - `'bfill'` to back-fill -# -# *Note that `'nearest'` is missing because we are not reindexing.* -# -# Here, we will use `'ffill'` to show how this works: - -# %% -df_deduped.assign( - TMAX=lambda x: x.TMAX.fillna(method='ffill'), - TMIN=lambda x: x.TMIN.fillna(method='ffill') -).head() - -# %% [markdown] -# We can use `np.nan_to_num()` to turn `np.nan` into 0 and `-np.inf`/`np.inf` -# into large negative or positive finite numbers: - -# %% -df_deduped.assign( - SNWD=lambda x: np.nan_to_num(x.SNWD) -).head() - -# %% [markdown] -# Depending on the data we are working with, we can use the `clip()` method as -# an alternative to `np.nan_to_num()`. The `clip()` method makes it possible to -# cap values at a specific minimum and/or maximum threshold. Since `SNWD` can't -# be negative, let's use `clip()` to enforce a lower bound of zero. To show how -# the upper bound works, let's use the value of `SNOW`: - -# %% -df_deduped.assign( - SNWD=lambda x: x.SNWD.clip(0, x.SNOW) -).head() - -# %% [markdown] -# We can couple `fillna()` with other types of calculations. Here we replace -# missing values of `TMAX` with the median of all `TMAX` values, `TMIN` with -# the median of all `TMIN` values, and `TOBS` to the average of the `TMAX` and -# `TMIN` values. Since we place `TOBS` last, we have access to the imputed -# values for `TMIN` and `TMAX` in the calculation: - -# %% -df_deduped.assign( - TMAX=lambda x: x.TMAX.fillna(x.TMAX.median()), - TMIN=lambda x: x.TMIN.fillna(x.TMIN.median()), - # average of TMAX and TMIN - TOBS=lambda x: x.TOBS.fillna((x.TMAX + x.TMIN) / 2) -).head() - -# %% [markdown] -# We can also use `apply()` for running the same calculation across columns. -# For example, let's fill all missing values with their rolling 7-day median of -# their values, setting the number of periods required for the calculation to 0 -# to ensure we don't introduce more extra `NaN` values. Rolling calculations -# will be covered later on, so this is a preview: - -# %% -df_deduped.apply( - # rolling calculations will be covered later on, this is a rolling 7-day median - # we set min_periods (# of periods required for calculation) to 0 so we - # always get a result - lambda x: x.fillna(x.rolling(7, min_periods=0).median()) -).head(10) - -# %% [markdown] -# The last strategy we could try is interpolation with the `interpolate()` -# method. We specify the `method` parameter with the interpolation strategy to -# use. There are many options, but we will stick with the default of -# `'linear'`, which will treat values as evenly spaced and place missing values -# in the middle of existing ones. We have some missing data, so we will reindex -# first. Look at January 9th, which we didn't have before—the values for -# `TMAX`, `TMIN`, and `TOBS` are the average of values the day prior (January -# 8th) and the day after (January 10th): - -# %% -df_deduped\ - .reindex(pd.date_range('2018-01-01', '2018-12-31', freq='D'))\ - .apply(lambda x: x.interpolate())\ - .head(10) - - diff --git a/scripts/intro_to_plotly_express.py b/scripts/intro_to_plotly_express.py deleted file mode 100644 index 9f2bb46..0000000 --- a/scripts/intro_to_plotly_express.py +++ /dev/null @@ -1,329 +0,0 @@ -# %% -import plotly.express as px - -df = px.data.gapminder().query("year==2007") - -# %% -df.columns - -# %% -df.describe() - -# %% -px.strip(df, x="lifeExp", hover_name="country") - -# %% -px.strip(df, x="lifeExp", color="continent", hover_name="country") - -# %% -px.histogram(df, x="lifeExp", color="continent", hover_name="country") - - -# %% -px.histogram(df, x="lifeExp", color="continent", hover_name="country", marginal="rug") - -# %% -px.histogram( - df, x="lifeExp", y="pop", color="continent", hover_name="country", marginal="rug" -) - -# %% -px.histogram( - df, - x="lifeExp", - y="pop", - color="continent", - hover_name="country", - marginal="rug", - facet_col="continent", -) - -# %% -px.bar(df, color="lifeExp", x="pop", y="continent", hover_name="country") - -# %% -px.sunburst( - df, - color="lifeExp", - values="pop", - path=["continent", "country"], - hover_name="country", - height=500, -) - - -# %% -px.treemap( - df, - color="lifeExp", - values="pop", - path=["continent", "country"], - hover_name="country", - height=500, -) - -# %% -px.choropleth( - df, color="lifeExp", locations="iso_alpha", hover_name="country", height=500 -) - - -# %% -px.scatter(df, x="gdpPercap", y="lifeExp", hover_name="country", height=500) - -# %% -px.scatter( - df, - x="gdpPercap", - y="lifeExp", - hover_name="country", - color="continent", - size="pop", - height=500, -) - -# %% [markdown] -# We can see that the curve follows a logarithmic path, so make `log_x=True` to -# straighten out the line to view the relationships in an easier manner. In the -# graph below we can view the [monotic and nonmonotonic -# relationships](https://www.statology.org/monotonic-relationship/) in the -# dataset. -# - -# %% -px.scatter( - df, - x="gdpPercap", - y="lifeExp", - hover_name="country", - color="continent", - size="pop", - size_max=60, - log_x=True, - height=500, -) - -# %% -fig = px.scatter( - df, - x="gdpPercap", - y="lifeExp", - hover_name="country", - color="continent", - size="pop", - size_max=60, - log_x=True, - height=500, -) - -# %% [markdown] -# This will allow you to inspect the values for each of these cells, -# unfortunately this is a great deal easier to see in JupyterLab. -# - -# %% -fig.show("json") - -# %% -import plotly.express as px - -df = px.data.gapminder().query("year == 2007") - -fig = px.scatter( - df, - x="gdpPercap", - y="lifeExp", - color="continent", - log_x=True, - size="pop", - size_max=60, - hover_name="country", - height=600, - width=1000, - template="simple_white", - color_discrete_sequence=px.colors.qualitative.G10, - title="Health vs Wealth 2007", - labels=dict( - continent="Continent", - pop="Population", - gdpPercap="GDP per Capita (US$, price-adjusted)", - lifeExp="Life Expectancy (years)", - ), -) - -fig.update_layout( - font_family="Rockwell", - legend=dict( - orientation="h", title="", y=1.1, x=1, xanchor="right", yanchor="bottom" - ), -) -fig.update_xaxes(tickprefix="$", range=[2, 5], dtick=1) -fig.update_yaxes(range=[30, 90]) -fig.add_hline( - (df["lifeExp"] * df["pop"]).sum() / df["pop"].sum(), line_width=1, line_dash="dot" -) -fig.add_vline( - (df["gdpPercap"] * df["pop"]).sum() / df["pop"].sum(), line_width=1, line_dash="dot" -) -fig.show() - -# fig.write_image("gapminder_2007.svg") # static export -# fig.write_html("gapminder_2007.html") # interactive export -# fig.write_json("gapminder_2007.json") # serialized export - -# %% [markdown] -# ## Animations in Plotly Express -# -# - -# %% -df_animation = px.data.gapminder() - -anim_fig = px.scatter( - df_animation, - x="gdpPercap", - y="lifeExp", - title="Health vs Wealth from 1952 to 2007", - labels=dict( - continent="Continent", - pop="Population", - gdpPercap="GDP per Capita (US$, price-adjusted)", - lifeExp="Life Expectancy (years)", - ), - animation_frame="year", - animation_group="country", - size="pop", - color="continent", - hover_name="country", - height=600, - width=1000, - template="simple_white", - color_discrete_sequence=px.colors.qualitative.G10, - log_x=True, - size_max=60, - range_x=[100, 100000], - range_y=[25, 90], -) - -anim_fig.update_layout( - font_family="Rockwell", - legend=dict( - orientation="h", title="", y=1.1, x=1, xanchor="right", yanchor="bottom" - ), -) -anim_fig.update_xaxes(tickprefix="$", range=[2, 5], dtick=1) - -# %% -anim_fig.write_html( - "gapminder_animation.html", auto_play=False -) # You're able to export this animation. - -# %% -px.defaults.height = 600 - -# %% -import plotly.express as px - -z = [ - [0.1, 0.3, 0.5, 0.7, 0.9], - [1, 0.8, 0.6, 0.4, 0.2], - [0.2, 0, 0.5, 0.7, 0.9], - [0.9, 0.8, 0.4, 0.2, 0], - [0.3, 0.4, 0.5, 0.7, 1], -] - -fig = px.imshow(z, text_auto=True) -fig.show() - -# %% -import plotly.express as px - -df = px.data.wind() -fig = px.bar_polar( - df, - r="frequency", - theta="direction", - height=600, - color="strength", - template="plotly_dark", - color_discrete_sequence=px.colors.sequential.Plasma_r, -) -fig.show() - -# %% -df = px.data.iris() -fig = px.parallel_coordinates( - df, - color="species_id", - labels={ - "species_id": "Species", - "sepal_width": "Sepal Width", - "sepal_length": "Sepal Length", - "petal_width": "Petal Width", - "petal_length": "Petal Length", - }, - color_continuous_scale=px.colors.diverging.Tealrose, - color_continuous_midpoint=2, -) -fig.show() - -# %% -df = px.data.tips() -fig = px.parallel_categories( - df, color="size", color_continuous_scale=px.colors.sequential.Inferno -) -fig.show() - -# %% -df = px.data.iris() -fig = px.parallel_coordinates( - df, - color="species_id", - labels={ - "species_id": "Species", - "sepal_width": "Sepal Width", - "sepal_length": "Sepal Length", - "petal_width": "Petal Width", - "petal_length": "Petal Length", - }, - color_continuous_scale=px.colors.diverging.Tealrose, - color_continuous_midpoint=2, -) -fig.show() - -# %% -df = px.data.tips() -fig = px.parallel_categories( - df, color="size", color_continuous_scale=px.colors.sequential.Inferno -) -fig.show() - -# %% -df = px.data.election() -fig = px.scatter_ternary( - df, - a="Joly", - b="Coderre", - c="Bergeron", - color="winner", - size="total", - hover_name="district", - size_max=15, - color_discrete_map={"Joly": "blue", "Bergeron": "green", "Coderre": "red"}, -) -fig.show() - -# %% -df = px.data.election() -fig = px.scatter_3d( - df, - x="Joly", - y="Coderre", - z="Bergeron", - color="winner", - size="total", - hover_name="district", - symbol="result", - color_discrete_map={"Joly": "blue", "Bergeron": "green", "Coderre": "red"}, -) -fig.show() diff --git a/scripts/introducing_matplotlib.py b/scripts/introducing_matplotlib.py deleted file mode 100644 index 4edc11a..0000000 --- a/scripts/introducing_matplotlib.py +++ /dev/null @@ -1,184 +0,0 @@ -# %% [markdown] -# # Getting Started with Matplotlib -# -# Pandas uses `matplotlib` to create visualizations. Therefore, before we learn how to plot with `pandas`, it's important to understand how `matplotlib` works at a high-level, which is the focus of this notebook. -# -# -# ## About the Data -# In this notebook, we will be working with 2 datasets: -# - Facebook's stock price throughout 2018 (obtained using the [`stock_analysis` package](https://github.com/stefmolin/stock-analysis)) -# - Earthquake data from September 18, 2018 - October 13, 2018 (obtained from the US Geological Survey (USGS) using the [USGS API](https://earthquake.usgs.gov/fdsnws/event/1/)) -# -# ## Setup -# We need to import `matplotlib.pyplot` for plotting. - -# %% -import matplotlib.pyplot as plt -import pandas as pd - -# %% [markdown] -# ## Plotting lines - -# %% -fb = pd.read_csv("../data/fb_stock_prices_2018.csv", index_col="date", parse_dates=True) - -plt.plot(fb.index, fb.open) -plt.show() - -# %% [markdown] -# Since we are working in a Jupyter notebook, we can use the magic command `%matplotlib inline` once and not have to call `plt.show()` for each plot. - -# %% -import matplotlib.pyplot as plt -import pandas as pd - -fb = pd.read_csv("../data/fb_stock_prices_2018.csv", index_col="date", parse_dates=True) -plt.plot(fb.index, fb.open) - -# %% [markdown] -# ## Scatter plots - -# %% [markdown] -# We can pass in a string specifying the style of the plot. This is of the form `[marker][linestyle][color]`. For example, we can make a black dashed line with `'--k'` or a red scatter plot with `'or'`: - -# %% -plt.plot("high", "low", "or", data=fb.head(20)) - -# %% [markdown] -# Here are some examples of how you make a format string: -# -# | Marker | Linestyle | Color | Format String | Result | -# | :---: | :---: | :---: | :---: | --- | -# | | `-` | `b` | `-b` | blue solid line| -# | `.` | | `k` | `.k` | black points| -# | | `--` | `r` | `--r` | red dashed line| -# | `o` | `-` | `g` | `o-g` | green solid line with circles| -# | | `:` | `m` | `:m` | magenta dotted line| -# |`x` | `-.` | `c` | `x-.c` | cyan dot-dashed line with x's| -# -# Note that we can also use format strings of the form `[color][marker][linestyle]`, but the parsing by `matplotlib` (in rare cases) might not be what we were aiming for. Consult the *Notes* section in the [documentation](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.plot.html) for the complete list of options. -# ## Histograms - -# %% -quakes = pd.read_csv("../data/earthquakes.csv") -plt.hist(quakes.query('magType == "ml"').mag) - -# %% [markdown] -# ### Bin size matters -# Notice how our assumptions of the distribution of the data can change based on the number of bins (look at the drop between the two highest peaks on the righthand plot): - -# %% -x = quakes.query('magType == "ml"').mag -fig, axes = plt.subplots(1, 2, figsize=(10, 3)) -for ax, bins in zip(axes, [7, 35]): - ax.hist(x, bins=bins) - ax.set_title(f"bins param: {bins}") - -# %% [markdown] -# ## Plot components -# ### `Figure` -# Top-level object that holds the other plot components. - -# %% -fig = plt.figure() - -# %% [markdown] -# ### `Axes` -# Individual plots contained within the `Figure`. -# -# ## Creating subplots -# Simply specify the number of rows and columns to create: - -# %% -fig, axes = plt.subplots(1, 2) - -# %% [markdown] -# As an alternative to using `plt.subplots()` we can add `Axes` objects to the `Figure` object on our own. This allows for some more complex layouts, such as picture in picture: - -# %% -fig = plt.figure(figsize=(3, 3)) -outside = fig.add_axes([0.1, 0.1, 0.9, 0.9]) -inside = fig.add_axes([0.7, 0.7, 0.25, 0.25]) - -# %% [markdown] -# ## Creating Plot Layouts with `gridspec` -# We can create subplots with varying sizes as well: - -# %% -fig = plt.figure(figsize=(8, 8)) -gs = fig.add_gridspec(3, 3) -top_left = fig.add_subplot(gs[0, 0]) -mid_left = fig.add_subplot(gs[1, 0]) -top_right = fig.add_subplot(gs[:2, 1:]) -bottom = fig.add_subplot(gs[2, :]) - -# %% [markdown] -# ## Saving plots -# Use `plt.savefig()` to save the last created plot. To save a specific `Figure` object, use its `savefig()` method. Which supports 'png', 'pdf', 'svg', and 'eps' filetypes. - -# %% -fig.savefig("empty.png") -fig.savefig("empty.pdf") -fig.savefig("empty.svg") -fig.savefig("empty.eps") - -# %% [markdown] -# ## Cleaning up -# It's important to close resources when we are done with them. We use `plt.close()` to do so. If we pass in nothing, it will close the last plot, but we can pass in the specific `Figure` object to close or say `'all'` to close all `Figure` objects that are open. Let's close all the `Figure` objects that are open with `plt.close()`: - -# %% -plt.close("all") - -# %% [markdown] -# ## Additional plotting options -# ### Specifying figure size -# Just pass the `figsize` argument to `plt.figure()`. It's a tuple of `(width, height)`: - -# %% -fig = plt.figure(figsize=(10, 4)) - -# %% [markdown] -# This can be specified when creating subplots as well: - -# %% -fig, axes = plt.subplots(1, 2, figsize=(10, 4)) - -# %% [markdown] -# ### `rcParams` -# A small subset of all the available plot settings (shuffling to get a good variation of options): - -# %% -import random -import matplotlib as mpl - -rcparams_list = list(mpl.rcParams.keys()) -random.seed(20) # make this repeatable -random.shuffle(rcparams_list) -sorted(rcparams_list[:20]) - -# %% [markdown] -# We can check the current default `figsize` using `rcParams`: - -# %% -mpl.rcParams["figure.figsize"] - -# %% [markdown] -# We can also update this value to change the default (until the kernel is restarted): - -# %% -mpl.rcParams["figure.figsize"] = (300, 10) -mpl.rcParams["figure.figsize"] - -# %% [markdown] -# Use `rcdefaults()` to restore the defaults. Note this is slightly different than before because running `%matplotlib inline` sets a different value for `figsize` ([see more](https://github.com/ipython/ipykernel/blob/master/ipykernel/pylab/config.py#L42-L56)). After we reset, we are going back to the default value of `figsize` before that import: - -# %% -mpl.rcdefaults() -mpl.rcParams["figure.figsize"] - -# %% [markdown] -# This can also be done via `pyplot`: - -# %% -plt.rc("figure", figsize=(20, 20)) # change `figsize` default to (20, 20) -plt.rcdefaults() # reset the default diff --git a/scripts/making_dataframes_from_api_requests.py b/scripts/making_dataframes_from_api_requests.py deleted file mode 100644 index b7f23d7..0000000 --- a/scripts/making_dataframes_from_api_requests.py +++ /dev/null @@ -1,70 +0,0 @@ -# %% [markdown] -# # Making Pandas DataFrames from API Requests -# In this example, we will use the U.S. Geological Survey's API to grab a JSON object of earthquake data and convert it to a `pandas.DataFrame`. -# -# USGS API: https://earthquake.usgs.gov/fdsnws/event/1/ - -# %% [markdown] -# ### Get Data from API - -# %% -import datetime as dt -import pandas as pd -import requests - -yesterday = dt.date.today() - dt.timedelta(days=1) -api = "https://earthquake.usgs.gov/fdsnws/event/1/query" -payload = { - "format": "geojson", - "starttime": yesterday - dt.timedelta(days=30), - "endtime": yesterday, -} -response = requests.get(api, params=payload) - -# let's make sure the request was OK -response.status_code - -# %% [markdown] -# Response of 200 means OK, so we can pull the data out of the result. Since we asked the API for a JSON payload, we can extract it from the response with the `json()` method. -# -# ### Isolate the Data from the JSON Response -# We need to check the structures of the response data to know where our data is. - -# %% -earthquake_json = response.json() -earthquake_json.keys() - -# %% [markdown] -# The USGS API provides information about our request in the `metadata` key. Note that your result will be different, regardless of the date range you chose, because the API includes a timestamp for when the data was pulled: - -# %% -earthquake_json["metadata"] - -# %% [markdown] -# Each element in the JSON array `features` is a row of data for our dataframe. - -# %% -type(earthquake_json["features"]) - -# %% [markdown] -# Your data will be different depending on the date you run this. - -# %% -earthquake_json["features"][0] - -# %% [markdown] -# ### Convert to DataFrame -# We need to grab the `properties` section out of every entry in the `features` JSON array to create our dataframe. - -# %% -earthquake_properties_data = [ - quake["properties"] for quake in earthquake_json["features"] -] -df = pd.DataFrame(earthquake_properties_data) -df.head() - -# %% [markdown] -# ### (Optional) Write Data to CSV - -# %% -df.to_csv("earthquakes.csv", index=False) diff --git a/scripts/one_hot_encoding.py b/scripts/one_hot_encoding.py deleted file mode 100644 index 1af4ff5..0000000 --- a/scripts/one_hot_encoding.py +++ /dev/null @@ -1,78 +0,0 @@ -# %% -import pandas as pd - -print(pd.__version__) - -# %% -disengagements = pd.read_excel( - "../data/cassi-autonomous-shuttle/autonomous_shuttle_disengagement.xlsx", - usecols=[ - "Incident Datetime", - "Location", - "Weather", - "Vehicle Speed in Miles per Hour", - "Initiated by", - "Cause", - ], - parse_dates=True, -) -disengagements - -# %% -disengagements.dtypes - -# %% -disengagements["Incident Datetime"] = pd.to_datetime( - disengagements["Incident Datetime"], utc=True -) -disengagements["Initiated by"] = disengagements["Initiated by"].astype("category") -disengagements["Cause"] = disengagements["Cause"].astype("category") -disengagements.dtypes - -# %% -disengagements = disengagements.assign( - week_of_year=disengagements["Incident Datetime"].dt.isocalendar().week, - week_of_pilot=lambda x: disengagements["Incident Datetime"].dt.isocalendar().week - - 9, -) -disengagements - -# %% -disengagements["Cause"] - -# %% -disengagements["Cause"].cat.categories - -# %% -disengagements_datetime_is_index = disengagements.set_index("Incident Datetime") -disengagements_datetime_is_index - -# %% -disengagements_datetime_is_index.index = ( - disengagements_datetime_is_index.index.tz_convert(tz="US/Eastern") -) -disengagements_datetime_is_index - -# %% -disengagements_datetime_is_index.dtypes - -# %% -one_hot = disengagements_datetime_is_index.Weather.str.get_dummies(sep=";") -one_hot - -# %% -one_hot.columns = "Weather_" + one_hot.columns -one_hot - -# %% -disengagements_datetime_is_index = disengagements_datetime_is_index.drop( - ["Weather", "Initiated by"], axis=1 -) -cassi_data = pd.concat([disengagements_datetime_is_index, one_hot], axis=1) -cassi_data - -# %% -cassi_data.index = cassi_data.index.tz_convert(tz="UTC") - -# %% -cassi_data diff --git a/scripts/pandas_plotting_module.py b/scripts/pandas_plotting_module.py deleted file mode 100644 index c33357d..0000000 --- a/scripts/pandas_plotting_module.py +++ /dev/null @@ -1,77 +0,0 @@ -# %% [markdown] -# # The `pandas.plotting` module -# Pandas provides some extra plotting functions for some new plot types. -# -# ## About the Data -# In this notebook, we will be working with Facebook's stock price throughout 2018 (obtained using the [`stock_analysis` package](https://github.com/stefmolin/stock-analysis)). -# -# ## Setup - -# %% -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd - -fb = pd.read_csv("../data/fb_stock_prices_2018.csv", index_col="date", parse_dates=True) - -# %% [markdown] -# ## Scatter matrix -# Easily create scatter plots between all columns in the dataset: - -# %% -from pandas.plotting import scatter_matrix - -scatter_matrix(fb, figsize=(10, 10)) - -# %% [markdown] -# Changing the diagonal from histograms to KDE: - -# %% -scatter_matrix(fb, figsize=(10, 10), diagonal="kde") - -# %% [markdown] -# ## Lag plot -# Lag plots let us see how the variable correlates with past observations of itself. Random data has no pattern: - -# %% -from pandas.plotting import lag_plot - -np.random.seed(0) # make this repeatable -lag_plot(pd.Series(np.random.random(size=200))) - -# %% [markdown] -# Data with some level of correlation to itself (autocorrelation) may have patterns. Stock prices are highly autocorrelated: - -# %% -lag_plot(fb.close) - -# %% [markdown] -# The default lag is 1, but we can alter this with the `lag` parameter. Let's look at a 5 day lag (a week of trading activity): - -# %% -lag_plot(fb.close, lag=5) - -# %% [markdown] -# ## Autocorrelation plots -# We can use the autocorrelation plot to see if this relationship may be meaningful or is just noise. Random data will not have any significant autocorrelation (it stays within the bounds below): - -# %% -from pandas.plotting import autocorrelation_plot - -np.random.seed(0) # make this repeatable -autocorrelation_plot(pd.Series(np.random.random(size=200))) - -# %% [markdown] -# Stock data, on the other hand, does have significant autocorrelation: - -# %% -autocorrelation_plot(fb.close) - -# %% [markdown] -# ## Bootstrap plot -# This plot helps us understand the uncertainty in our summary statistics: - -# %% -from pandas.plotting import bootstrap_plot - -fig = bootstrap_plot(fb.volume, fig=plt.figure(figsize=(10, 6))) diff --git a/scripts/plotting_with_pandas.py b/scripts/plotting_with_pandas.py deleted file mode 100644 index 35ee1cc..0000000 --- a/scripts/plotting_with_pandas.py +++ /dev/null @@ -1,525 +0,0 @@ -# %% [markdown] -# # Plotting with Pandas -# The `plot()` method is available on `Series` and `DataFrame` objects. Many of the parameters get passed down to `matplotlib`. The `kind` argument let's us vary the plot type. Here are some commonly used parameters: -# -# | Parameter | Purpose | Data Type | -# | --- | --- | --- | -# | `kind` | Determines the plot type | String | -# | `x`/`y` | Column(s) to plot on the *x*-axis/*y*-axis | String or list | -# | `ax` | Draws the plot on the `Axes` object provided | `Axes` | -# | `subplots` | Determines whether to make subplots | Boolean | -# | `layout` | Specifies how to arrange the subplots | Tuple of `(rows, columns)` | -# | `figsize` | Size to make the `Figure` object | Tuple of `(width, height)` | -# | `title` | The title of the plot or subplots | String for the plot title or a list of strings for subplot titles | -# | `legend` | Determines whether to show the legend | Boolean | -# | `label` | What to call an item in the legend | String if a single column is being plotted; otherwise, a list of strings | -# | `style` | `matplotlib` style strings for each item being plotted | String if a single column is being plotted; otherwise, a list of strings | -# | `color` | The color to plot the item in | String or red, green, blue tuple if a single column is being plotted; otherwise, a list | -# | `colormap` | The colormap to use | String or `matplotlib` colormap object | -# | `logx`/`logy`/`loglog` | Determines whether to use a logarithmic scale for the *x*-axis, *y*-axis, or both | Boolean | -# | `xticks`/`yticks` | Determines where to draw the ticks on the *x*-axis/*y*-axis | List of values | -# | `xlim`/`ylim` | The axis limits for the *x*-axis/*y*-axis | Tuple of the form `(min, max)` | -# | `rot` | The angle to write the tick labels at | Integer | -# | `sharex`/`sharey` | Determines whether to have subplots share the *x*-axis/*y*-axis | Boolean | -# | `fontsize` | Controls the size of the tick labels | Integer | -# | `grid` | Turns on/off the grid lines | Boolean | -# -# ## About the Data -# In this notebook, we will be working with 3 datasets: -# - Facebook's stock price throughout 2018 (obtained using the [`stock_analysis` package](https://github.com/stefmolin/stock-analysis)) -# - Earthquake data from September 18, 2018 - October 13, 2018 (obtained from the US Geological Survey (USGS) using the [USGS API](https://earthquake.usgs.gov/fdsnws/event/1/)) -# - European Centre for Disease Prevention and Control's (ECDC) [daily number of new reported cases of COVID-19 by country worldwide dataset](https://www.ecdc.europa.eu/en/publications-data/download-todays-data-geographic-distribution-covid-19-cases-worldwide) collected on September 19, 2020 via [this link](https://opendata.ecdc.europa.eu/covid19/casedistribution/csv) -# -# ## Setup - -# %% -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd - -fb = pd.read_csv("../data/fb_stock_prices_2018.csv", index_col="date", parse_dates=True) -quakes = pd.read_csv("../data/earthquakes.csv") -covid = ( - pd.read_csv("../data/covid19_cases.csv") - .assign(date=lambda x: pd.to_datetime(x.dateRep, format="%d/%m/%Y")) - .set_index("date") - .replace("United_States_of_America", "USA") - .sort_index()["2020-01-18":"2020-09-18"] -) - -# %% [markdown] -# ## Evolution over time -# Line plots help us see how a variable changes over time. They are the default for the `kind` argument, but we can pass `kind='line'` to be explicit in our intent: - -# %% -fb.plot( - kind="line", - y="open", - figsize=(10, 5), - style="-b", - legend=False, - title="Evolution of Facebook Open Price", -) - -# %% [markdown] -# We provided the `style` argument in the previous example; however, we can use the `color` and `linestyle` arguments to get the same result: - -# %% -fb.plot( - kind="line", - y="open", - figsize=(10, 5), - color="blue", - linestyle="solid", - legend=False, - title="Evolution of Facebook Open Price", -) - -# %% [markdown] -# We can also plot many lines at once by simply passing a list of the columns to plot: - -# %% -fb.first("1W").plot( - y=["open", "high", "low", "close"], - style=["o-b", "--r", ":k", ".-g"], - title="Facebook OHLC Prices during 1st Week of Trading 2018", -).autoscale() - -# %% [markdown] -# ### Creating subplots -# When plotting with `pandas`, creating subplots is simply a matter of passing `subplots=True` to the `plot()` method, and (optionally) specifying the `layout` in a tuple of `(rows, columns)`: - -# %% -fb.plot( - kind="line", - subplots=True, - layout=(3, 2), - figsize=(15, 10), - title="Facebook Stock 2018", -) - -# %% [markdown] -# Note that we didn't provide a specific column to plot and `pandas` plotted all of them for us. -# -# Sometimes we want to make subplots that each have a few variables in them for comparison. This can be achieved using the `ax` parameter. To illustrate this, let's take a look at daily new COVID-19 cases in China, Spain, Italy, the USA, Brazil, and India: - -# %% -new_cases_rolling_average = ( - covid.pivot_table( - index=covid.index, columns="countriesAndTerritories", values="cases" - ) - .rolling(7) - .mean() -) - -# %% [markdown] -# Since there is a lot of fluctuation in these values, we will plot the 7-day moving average of new cases using the `rolling()` method (discussed in chapter 4). Rather than create a separate plot for each country (which makes it harder to compare) or plot them all together (which will make it difficult to see the smaller values), we will plot countries that have had a similar number of cases in the same subplot: - -# %% -fig, axes = plt.subplots(1, 3, figsize=(15, 5)) - -new_cases_rolling_average[["China"]].plot(ax=axes[0], style="-.c") -new_cases_rolling_average[["Italy", "Spain"]].plot( - ax=axes[1], - style=["-", "--"], - title="7-day rolling average of new COVID-19 cases\n(source: ECDC)", -) -new_cases_rolling_average[["Brazil", "India", "USA"]].plot( - ax=axes[2], style=["--", ":", "-"] -) - -# %% [markdown] -# *NOTE: we specified the line styles here so that the lines can be distinguished in the text as a black and white image.* -# -# In the previous figure, we were able to compare countries with similar levels of new COVID-19 cases, but we couldn't compare all of them in the same plot due to scale. One way around this is to use an **area plot**, which makes it possible for us to visualize the overall 7-day rolling average of new COVID-19 cases and at the same time how much each country is contributing to the total. In the interest of readability, we will group Italy and Spain together and create another category for countries other than the USA, Brazil, and India. The combined height of the plot areas is the overall value, and the height of given shaded region is the value for the individual country. - -# %% -plot_cols = ["Brazil", "India", "Italy & Spain", "USA", "Other"] -grouped = ["Italy", "Spain"] -other_cols = [col for col in new_cases_rolling_average.columns if col not in plot_cols] - -new_cases_rolling_average.sort_index(axis=1).assign( - **{ - "Italy & Spain": lambda x: x[grouped].sum(axis=1), - "Other": lambda x: x[other_cols].drop(columns=grouped).sum(axis=1), - } -)[plot_cols].plot( - kind="area", - figsize=(15, 5), - title="7-day rolling average of new COVID-19 cases\n(source: ECDC)", -) - -# %% [markdown] -# Another way to visualize evolution over time is to look at the cumulative sum over time. Let's plot the cumulative number of COVID-19 cases in China, Spain, Italy, the USA, Brazil, and India, using `ax` to create subplots as we did in the previous example. - -# %% -fig, axes = plt.subplots(1, 3, figsize=(15, 3)) - -cumulative_covid_cases = ( - covid.groupby(["countriesAndTerritories", pd.Grouper(freq="1D")]) - .cases.sum() - .unstack(0) - .apply("cumsum") -) - -cumulative_covid_cases[["China"]].plot(ax=axes[0], style="-.c") -cumulative_covid_cases[["Italy", "Spain"]].plot( - ax=axes[1], style=["-", "--"], title="Cumulative COVID-19 Cases\n(source: ECDC)" -) -cumulative_covid_cases[["Brazil", "India", "USA"]].plot( - ax=axes[2], style=["--", ":", "-"] -) - -# %% [markdown] -# *NOTE: we specified the line styles here so that the lines can be distinguished in the text as a black and white image.* -# -# ## Visualizing relationships between variables -# ### Scatter plots -# We make scatter plots to help visualize the relationship between two variables. Creating scatter plots requires we pass in `kind='scatter'` along with a column for the x-axis and a column for the y-axis: - -# %% -fb.assign(max_abs_change=fb.high - fb.low).plot( - kind="scatter", - x="volume", - y="max_abs_change", - title="Facebook Daily High - Low vs. Volume Traded", -) - -# %% [markdown] -# The relationship doesn't seem to be linear, but we can try a log transform on the x-axis since the scales of the axes are very different. With `pandas`, we simply pass in `logx=True`: - -# %% -fb.assign(max_abs_change=fb.high - fb.low).plot( - kind="scatter", - x="volume", - y="max_abs_change", - title="Facebook Daily High - Low vs. log(Volume Traded)", - logx=True, -) - -# %% [markdown] -# With `matplotlib`, we could use `plt.xscale('log')` to do the same thing. -# -# ### Adding Transparency to Plots with `alpha` -# Sometimes our plots have many overlapping values, but this can be impossible to see. This can be addressed by increasing the transparency of what we are plotting using the `alpha` parameter. It is a float in the range [0, 1] where 0 is completely transparent and 1 is completely opaque. By default this is 1, so let's put in a lower value and re-plot the scatter plot: - -# %% -fb.assign(max_abs_change=fb.high - fb.low).plot( - kind="scatter", - x="volume", - y="max_abs_change", - title="Facebook Daily High - Low vs. log(Volume Traded)", - logx=True, - alpha=0.25, -) - -# %% [markdown] -# ### Hexbins -# In the previous example, we can start to see the overlaps, but it is still difficult. Hexbins are another plot type that divide up the plot into hexagons, which are shaded according to the density of points there. With `pandas`, this is the `hexbin` value for the `kind` argument. It may also be necessary to tweak the `gridsize`, which determines the number of hexagons along the y-axis: - -# %% -fb.assign(log_volume=np.log(fb.volume), max_abs_change=fb.high - fb.low).plot( - kind="hexbin", - x="log_volume", - y="max_abs_change", - title="Facebook Daily High - Low vs. log(Volume Traded)", - colormap="gray_r", - gridsize=20, - sharex=False, # we have to pass this to see the x-axis -) - -# %% [markdown] -# ### Visualizing Correlations with Heatmaps -# Pandas doesn't offer heatmaps; however, if we are able to get our data into a matrix, we can use `matshow()` from matplotlib: - -# %% -fig, ax = plt.subplots(figsize=(20, 10)) - -# calculate the correlation matrix -fb_corr = fb.assign( - log_volume=np.log(fb.volume), max_abs_change=fb.high - fb.low -).corr() - -# create the heatmap and colorbar -im = ax.matshow(fb_corr, cmap="seismic") -im.set_clim(-1, 1) -fig.colorbar(im) - -# label the ticks with the column names -labels = [col.lower() for col in fb_corr.columns] -ax.set_xticks(ax.get_xticks()[1:-1]) # to handle bug in matplotlib -ax.set_xticklabels(labels, rotation=45) -ax.set_yticks(ax.get_yticks()[1:-1]) # to handle bug in matplotlib -ax.set_yticklabels(labels) - -# include the value of the correlation coefficient in the boxes -for (i, j), coef in np.ndenumerate(fb_corr): - ax.text( - i, - j, - rf"$\rho$ = {coef:.2f}", # raw (r), format (f) string - ha="center", - va="center", - color="white", - fontsize=14, - ) - -# %% [markdown] -# Accessing the values in the correlation matrix can be done with `loc[]`: - -# %% -fb_corr.loc["max_abs_change", ["volume", "log_volume"]] - -# %% [markdown] -# ## Visualizing distributions -# ### Histograms -# With the `pandas`, making histograms is as easy as passing `kind='hist'` to the `plot()` method: - -# %% -fb.volume.plot(kind="hist", title="Histogram of Daily Volume Traded in Facebook Stock") -plt.xlabel("Volume traded") # label the x-axis (discussed in chapter 6) - -# %% [markdown] -# We can overlap histograms to compare distributions provided we use the `alpha` parameter. For example, let's compare the usage and magnitude of the various measurement techniques (the `magType` column) in the data: - -# %% -fig, axes = plt.subplots(figsize=(8, 5)) - -for magtype in quakes.magType.unique(): - data = quakes.query(f'magType == "{magtype}"').mag - if not data.empty: - data.plot( - kind="hist", - ax=axes, - alpha=0.4, - label=magtype, - legend=True, - title="Comparing histograms of earthquake magnitude by magType", - ) - -plt.xlabel("magnitude") # label the x-axis (discussed in chapter 6) - -# %% [markdown] -# ### Kernel Density Estimation (KDE) -# We can pass `kind='kde'` for an estimate of the probability density function (PDF), which tells us the probability of getting a particular value: - -# %% -fb.high.plot(kind="kde", title="KDE of Daily High Price for Facebook Stock") -plt.xlabel("Price ($)") # label the x-axis (discussed in chapter 6) - -# %% [markdown] -# ### Adding to the result of `plot()` -# The `plot()` method returns an `Axes` object. We can store this for additional customization of the plot, or we can pass this into another call to `plot()` as the `ax` argument to add to the original plot. -# -# It can often be helpful to view the KDE superimposed on top of the histogram, which can be achieved with this strategy: - -# %% -ax = fb.high.plot(kind="hist", density=True, alpha=0.5) -fb.high.plot( - ax=ax, - kind="kde", - color="blue", - title="Distribution of Facebook Stock's Daily High Price in 2018", -) -plt.xlabel("Price ($)") # label the x-axis (discussed in chapter 6) - -# %% [markdown] -# ### Plotting the ECDF -# In some cases, we are more interested in the probability of getting less than or equal to that value (or greater than or equal), which we can see with the **cumulative disribution function (CDF)**. Using the `statsmodels` package, we can estimate the CDF giving us the **empirical cumulative distribution function (ECDF)**: - -# %% -from statsmodels.distributions.empirical_distribution import ECDF - -ecdf = ECDF(quakes.query('magType == "ml"').mag) -plt.plot(ecdf.x, ecdf.y) - -# axis labels (we will cover this in chapter 6) -plt.xlabel("mag") # add x-axis label -plt.ylabel("cumulative probability") # add y-axis label - -# add title (we will cover this in chapter 6) -plt.title("ECDF of earthquake magnitude with magType ml") - -# %% [markdown] -# This ECDF tells us the probability of getting an earthquake with magnitude of 3 or less using the `ml` scale is 98%: - -# %% -from statsmodels.distributions.empirical_distribution import ECDF - -ecdf = ECDF(quakes.query('magType == "ml"').mag) -plt.plot(ecdf.x, ecdf.y) - -# formatting below will all be covered in chapter 6 -# axis labels -plt.xlabel("mag") # add x-axis label -plt.ylabel("cumulative probability") # add y-axis label - -# add reference lines for interpreting the ECDF for mag <= 3 -plt.plot([3, 3], [0, 0.98], "--k", [-1.5, 3], [0.98, 0.98], "--k", alpha=0.4) - -# set axis ranges -plt.ylim(0, None) -plt.xlim(-1.25, None) - -# add a title -plt.title("P(mag <= 3) = 98%") - -# %% [markdown] -# ### Box plots -# To make box plots with `pandas`, we pass `kind='box'` to the `plot()` method: - -# %% -fb.iloc[:, :4].plot(kind="box", title="Facebook OHLC Prices Box Plot") -plt.ylabel("price ($)") # label the y-axis (discussed in chapter 6) - -# %% [markdown] -# If we pass in `notch=True`, we get a notched box plot. The notch represents a 95% confidence interval around the median, which can be helpful when comparing differences. For an introduction to interpreting a notched box plot, see this [Google sites page](https://sites.google.com/site/davidsstatistics/home/notched-box-plots) and this [Towards Data Science article](https://towardsdatascience.com/understanding-boxplots-5e2df7bcbd51). - -# %% -fb.iloc[:, :4].plot(kind="box", title="Facebook OHLC Prices Box Plot", notch=True) -plt.ylabel("price ($)") # label the y-axis (discussed in chapter 6) - -# %% [markdown] -# This can also be combined with a call to `groupby()`: - -# %% -fb.assign(volume_bin=pd.cut(fb.volume, 3, labels=["low", "med", "high"])).groupby( - "volume_bin" -).boxplot(column=["open", "high", "low", "close"], layout=(1, 3), figsize=(12, 3)) -plt.suptitle("Facebook OHLC Box Plots by Volume Traded", y=1.1) - -# %% [markdown] -# We can use this to see the distribution of magnitudes across the different measurement methods for earthquakes: - -# %% -quakes[["mag", "magType"]].groupby("magType").boxplot(figsize=(15, 8), subplots=False) -plt.title("Earthquake Magnitude Box Plots by magType") -plt.ylabel("magnitude") # label the y-axis (discussed in chapter 6) - -# %% [markdown] -# ## Counts and frequencies -# ### Bar charts -# Passing `kind='barh'` gives us horizontal bars while `kind='bar'` gives us vertical ones. Let's use horizontal bars to look at the top 15 places for earthquakes in our data: - -# %% -quakes.parsed_place.value_counts().iloc[14::-1,].plot( - kind="barh", - figsize=(10, 5), - title="Top 15 Places for Earthquakes " "(September 18, 2018 - October 13, 2018)", -) -plt.xlabel("earthquakes") # label the x-axis (discussed in chapter 6) - -# %% [markdown] -# We also have data on whether earthquakes were accompanied by tsunamis. Let's see what the top places for tsunamis are: - -# %% -quakes.groupby("parsed_place").tsunami.sum().sort_values().iloc[-10:,].plot( - kind="barh", - figsize=(10, 5), - title="Top 10 Places for Tsunamis " "(September 18, 2018 - October 13, 2018)", -) -plt.xlabel("tsunamis") # label the x-axis (discussed in chapter 6) - -# %% [markdown] -# Seeing that Indonesia is the top place for tsunamis during the time period we are looking at, we may want to look how many earthquakes and tsunamis Indonesia gets on a daily basis. We could show this as a line plot or with bars; since we don't want to interpolate, we will use bars here: - -# %% -indonesia_quakes = ( - quakes.query('parsed_place == "Indonesia"') - .assign(time=lambda x: pd.to_datetime(x.time, unit="ms"), earthquake=1) - .set_index("time") - .resample("1D") - .sum() -) - -# format the datetimes in the index for the x-axis -indonesia_quakes.index = indonesia_quakes.index.strftime("%b\n%d") - -indonesia_quakes.plot( - y=["earthquake", "tsunami"], - kind="bar", - figsize=(15, 3), - rot=0, - label=["earthquakes", "tsunamis"], - title="Earthquakes and Tsunamis in Indonesia " - "(September 18, 2018 - October 13, 2018)", -) - -# label the axes (discussed in chapter 6) -plt.xlabel("date") -plt.ylabel("count") - -# %% [markdown] -# ### Grouped Bars - -# %% -quakes.groupby(["parsed_place", "tsunami"]).mag.count().unstack().apply( - lambda x: x / x.sum(), axis=1 -).rename(columns={0: "no", 1: "yes"}).sort_values("yes", ascending=False)[ - 7::-1 -].plot.barh(title="Frequency of a tsunami accompanying an earthquake") - -# move legend to the right of the plot -plt.legend(title="tsunami?", bbox_to_anchor=(1, 0.65)) - -# label the axes (discussed in chapter 6) -plt.xlabel("percentage of earthquakes") -plt.ylabel("") - -# %% [markdown] -# Using the `kind` arugment for vertical bars when the labels for each bar are shorter: - -# %% -quakes.magType.value_counts().plot( - kind="bar", title="Earthquakes Recorded per magType", rot=0 -) - -# label the axes (discussed in chapter 6) -plt.xlabel("magType") -plt.ylabel("earthquakes") - -# %% [markdown] -# ### Stacked bars - -# %% -pivot = quakes.assign(mag_bin=lambda x: np.floor(x.mag)).pivot_table( - index="mag_bin", columns="magType", values="mag", aggfunc="count" -) -pivot.plot.bar( - stacked=True, - rot=0, - ylabel="earthquakes", - title="Earthquakes by integer magnitude and magType", -) - -# %% [markdown] -# #### Normalized stacked bars -# Plot the percentages to be better able to see the different `magTypes`. - -# %% -normalized_pivot = pivot.fillna(0).apply(lambda x: x / x.sum(), axis=1) -ax = normalized_pivot.plot.bar( - stacked=True, - rot=0, - figsize=(10, 5), - title="Percentage of earthquakes by integer magnitude for each magType", -) -ax.legend(bbox_to_anchor=(1, 0.8)) # move legend to the right of the plot -plt.ylabel("percentage") # label the y-axis (discussed in chapter 6) - -# %% [markdown] -# We can also create horizontal stacked bars and do so using `groupby()` and `unstack()`: - -# %% -quakes.groupby(["parsed_place", "tsunami"]).mag.count().unstack().apply( - lambda x: x / x.sum(), axis=1 -).rename(columns={0: "no", 1: "yes"}).sort_values("yes", ascending=False)[ - 7::-1 -].plot.barh(title="Frequency of a tsunami accompanying an earthquake", stacked=True) - -# move legend to the right of the plot -plt.legend(title="tsunami?", bbox_to_anchor=(1, 0.65)) - -# label the axes (discussed in chapter 6) -plt.xlabel("percentage of earthquakes") -plt.ylabel("") diff --git a/scripts/python_101.py b/scripts/python_101.py deleted file mode 100644 index 52e77bc..0000000 --- a/scripts/python_101.py +++ /dev/null @@ -1,567 +0,0 @@ -# %% [markdown] -# # Python 101 -# -# This is an optional notebook to get you up to speed with Python in case you are new to Python or need a refresher. The material here is a crash course in Python; I highly recommend the [official Python tutorial](https://docs.python.org/3/tutorial/) for a deeper dive. Consider reading [this page](https://docs.python.org/3/tutorial/appetite.html) in the Python docs for background on Python and bookmarking the [glossary](https://docs.python.org/3/glossary.html#glossary). -# -# ## Basic data types -# ### Numbers -# Numbers in Python can be represented as integers (e.g. `5`) or floats (e.g. `5.0`). We can perform operations on them: - -# %% -5 + 6 - -# %% -2.5 / 3 - -# %% [markdown] -# ### Booleans -# -# We can check for equality giving us a Boolean: - -# %% -5 == 6 - -# %% -5 < 6 - -# %% [markdown] -# These statements can be combined with logical operators: `not`, `and`, `or` - -# %% -(5 < 6) and not (5 == 6) - -# %% -False or True - -# %% -True or False - -# %% [markdown] -# ### Strings -# Using strings, we can handle text in Python. These values must be surrounded in quotes — single (`'...'`) is the standard, but double (`"..."`) works as well: - -# %% -"hello" - -# %% [markdown] -# We can also perform operations on strings. For example, we can see how long it is with `len()`: - -# %% -len("hello") - -# %% [markdown] -# We can select parts of the string by specifying the **index**. Note that in Python the 1st character is at index 0: - -# %% -"hello"[0] - -# %% [markdown] -# We can concatentate strings with `+`: - -# %% -"hello" + " " + "world" - -# %% [markdown] -# We can check if characters are in the string with the `in` operator: - -# %% -"h" in "hello" - -# %% [markdown] -# ## Variables -# Notice that just typing text causes an error. Errors in Python attempt to clue us in to what went wrong with our code. In this case, we have a `NameError` exception which tells us that `'hello'` is not defined. This means that [the Python interpreter](https://docs.python.org/3/tutorial/interpreter.html) looked for a **variable** named `hello`, but it didn't find one. - -# %% -hello - -# %% [markdown] -# Variables give us a way to store data types. We define a variable using the `variable_name = value` syntax: - -# %% -x = 5 -y = 7 -x + y - -# %% [markdown] -# The variable name cannot contain spaces; we usually use `_` instead. The best variable names are descriptive ones: - -# %% -book_title = "Hands-On Data Analysis with Pandas" - -# %% [markdown] -# Variables can be any data type. We can check which one it is with `type()`, which is a **function** (more on that later): - -# %% -type(x) - -# %% -type(book_title) - -# %% [markdown] -# If we need to see the value of a variable, we can print it using the `print()` function: - -# %% -print(book_title) - -# %% [markdown] -# ## Collections of Items -# -# ### Lists -# We can store a collection of items in a list: - -# %% -["hello", " ", "world"] - -# %% [markdown] -# The list can be stored in a variable. Note that the items in the list can be of different types: - -# %% -my_list = ["hello", 3.8, True, "Python"] -type(my_list) - -# %% [markdown] -# We can see how many elements are in the list with `len()`: - -# %% -len(my_list) - -# %% [markdown] -# We can also use the `in` operator to check if a value is in the list: - -# %% -"world" in my_list - -# %% [markdown] -# We can select items in the list just as we did with strings, by providing the index to select: - -# %% -my_list[1] - -# %% [markdown] -# Python also allows us to use negative values, so we can easily select the last one: - -# %% -my_list[-1] - -# %% [markdown] -# Another powerful feature of lists (and strings) is **slicing**. We can grab the middle 2 elements in the list: - -# %% -my_list[1:3] - -# %% [markdown] -# ... or every other one: - -# %% -my_list[::2] - -# %% [markdown] -# We can even select the list in reverse: - -# %% -my_list[::-1] - -# %% [markdown] -# Note: This syntax is `[start:stop:step]` where the selection is inclusive of the start index, but exclusive of the stop index. If `start` isn't provided, `0` is used. If `stop` isn't provided, the number of elements is used (4, in our case); this works because the `stop` is exclusive. If `step` isn't provided, it is 1. -# -# We can use the `join()` method on a string object to concatenate all the items of a list into single string. The string we call the `join()` method on will be used as the separator, here we separate with a pipe (|): - -# %% -"|".join(["x", "y", "z"]) - -# %% [markdown] -# ### Tuples -# Tuples are similar to lists; however, they can't be modified after creation i.e. they are **immutable**. Instead of square brackets, we use parenthesis to create tuples: - -# %% -my_tuple = ("a", 5) -type(my_tuple) - -# %% -my_tuple[0] - -# %% [markdown] -# Immutable objects can't be modified: - -# %% -my_tuple[0] = "b" - -# %% [markdown] -# ### Dictionaries -# We can store mappings of key-value pairs using dictionaries: - -# %% -shopping_list = { - "veggies": ["spinach", "kale", "beets"], - "fruits": "bananas", - "meat": 0, -} -type(shopping_list) - -# %% [markdown] -# To access the values associated with a specific key, we use the square bracket notation again: - -# %% -shopping_list["veggies"] - -# %% [markdown] -# We can extract all of the keys with `keys()`: - -# %% -shopping_list.keys() - -# %% [markdown] -# We can extract all of the values with `values()`: - -# %% -shopping_list.values() - -# %% [markdown] -# Finally, we can call `items()` to get back pairs of (key, value) pairs: - -# %% -shopping_list.items() - -# %% [markdown] -# ### Sets -# A set is a collection of unique items; a common use is to remove duplicates from a list. These are written with curly braces also, but notice there is no key-value mapping: - -# %% -my_set = {1, 1, 2, "a"} -type(my_set) - -# %% [markdown] -# How many items are in this set? - -# %% -len(my_set) - -# %% [markdown] -# We put in 4 items but the set only has 3 because duplicates are removed: - -# %% -my_set - -# %% [markdown] -# We can check if a value is in the set: - -# %% -2 in my_set - -# %% [markdown] -# ## Functions -# We can define functions to package up our code for reuse. We have already seen some functions: `len()`, `type()`, and `print()`. They are all functions that take **arguments**. Note that functions don't need to accept arguments, in which case they are called without passing in anything (e.g. `print()` versus `print(my_string)`). -# -# *Aside: we can also create lists, sets, dictionaries, and tuples with functions: `list()`, `set()`, `dict()`, and `tuple()`* -# -# ### Defining functions -# -# We use the `def` keyword to define functions. Let's create a function called `add()` with 2 parameters, `x` and `y`, which will be the names the code in the function will use to refer to the arguments we pass in when calling it: - - -# %% -def add(x, y): - """This is a docstring. It is used to explain how the code works and is optional (but encouraged).""" - # this is a comment; it allows us to annotate the code - print("Performing addition") - return x + y - - -# %% [markdown] -# Once we run the code above, our function is ready to use: - -# %% -type(add) - -# %% [markdown] -# Let's add some numbers: - -# %% -add(1, 2) - -# %% [markdown] -# ### Return values -# We can store the result in a variable for later: - -# %% -result = add(1, 2) - -# %% [markdown] -# Notice the print statement wasn't captured in `result`. This variable will only have what the function **returns**. This is what the `return` line in the function definition did: - -# %% -result - -# %% [markdown] -# Note that functions don't have to return anything. Consider `print()`: - -# %% -print_result = print("hello world") - -# %% [markdown] -# If we take a look at what we got back, we see it is a `NoneType` object: - -# %% -type(print_result) - -# %% [markdown] -# In Python, the value `None` represents null values. We can check if our variable *is* `None`: - -# %% -print_result is None - -# %% [markdown] -# *Warning: make sure to use comparison operators (e.g. >, >=, <, <=, ==, !=) to compare to values other than `None`.* -# -# ### Function arguments -# -# *Note that function arguments can be anything, even other functions. We will see several examples of this in the text.* -# -# The function we defined requires arguments. If we don't provide them all, it will cause an error: - -# %% -add(1) - -# %% [markdown] -# We can use `help()` to check what arguments the function needs (notice the docstring ends up here): - -# %% -help(add) - -# %% [markdown] -# We will also get errors if we pass in data types that `add()` can't work with: - -# %% -add(set(), set()) - -# %% [markdown] -# We will discuss error handling in the text. -# -# ## Control Flow Statements -# Sometimes we want to vary the path the code takes based on some criteria. For this we have `if`, `elif`, and `else`. We can use `if` on its own: - - -# %% -def make_positive(x): - """Returns a positive x""" - if x < 0: - x *= -1 - return x - - -# %% [markdown] -# Calling this function with negative input causes the code under the `if` statement to run: - -# %% -make_positive(-1) - -# %% [markdown] -# Calling this function with positive input skips the code under the `if` statement, keeping the number positive: - -# %% -make_positive(2) - -# %% [markdown] -# Sometimes we need an `else` statement as well: - - -# %% -def add_or_subtract(operation, x, y): - if operation == "add": - return x + y - else: - return x - y - - -# %% [markdown] -# This triggers the code under the `if` statement: - -# %% -add_or_subtract("add", 1, 2) - -# %% [markdown] -# Since the Boolean check in the `if` statement was `False`, this triggers the code under the `else` statement: - -# %% -add_or_subtract("subtract", 1, 2) - -# %% [markdown] -# For more complicated logic, we can also use `elif`. We can have any number of `elif` statements. Optionally, we can include `else`. - - -# %% -def calculate(operation, x, y): - if operation == "add": - return x + y - elif operation == "subtract": - return x - y - elif operation == "multiply": - return x * y - elif operation == "division": - return x / y - else: - print("This case hasn't been handled") - - -# %% [markdown] -# The code keeps checking the conditions in the `if` statements from top to bottom until it finds `multiply`: - -# %% -calculate("multiply", 3, 4) - -# %% [markdown] -# The code keeps checking the conditions in the `if` statements from top to bottom until it hits the `else` statement: - -# %% -calculate("power", 3, 4) - -# %% [markdown] -# ## Loops -# ### `while` loops -# With `while` loops, we can keep running code until some stopping condition is met: - -# %% -done = False -value = 2 -while not done: - print("Still going...", value) - value *= 2 - if value > 10: - done = True - -# %% [markdown] -# Note this can also be written as, by moving the condition to the `while` statement: - -# %% -value = 2 -while value < 10: - print("Still going...", value) - value *= 2 - -# %% [markdown] -# ### `for` loops -# With `for` loops, we can run our code *for each* element in a collection: - -# %% -for i in range(5): - print(i) - -# %% [markdown] -# We can use `for` loops with lists, tuples, sets, and dictionaries as well: - -# %% -for element in my_list: - print(element) - -# %% -for key, value in shopping_list.items(): - print("For", key, "we need to buy", value) - -# %% [markdown] -# With `for` loops, we don't have to worry about checking if we have reached the stopping condition. Conversely, `while` loops can cause infinite loops if we don't remember to update variables. -# -# ## Imports -# We have been working with the portion of Python that is available without importing additional functionality. The Python standard library that comes with the install of Python is broken up into several **modules**, but we often only need a few. We can import whatever we need: a module in the standard library, a 3rd-party library, or code that we wrote. This is done with an `import` statement: - -# %% -import math - -print(math.pi) - -# %% [markdown] -# If we only need a small piece from that module, we can do the following instead: - -# %% -from math import pi - -print(pi) - -# %% [markdown] -# *Warning: anything you import is added to the namespace, so if you create a new variable/function/etc. with the same name it will overwrite the previous value. For this reason, we have to be careful with variable names e.g. if you name something `sum`, you won't be able to add using the `sum()` built-in function anymore. Using notebooks or an IDE will help you avoid these issues with syntax highlighting.* -# -# ## Installing 3rd-party Packages -# -# We can use [`pip`](https://pip.pypa.io/en/stable/reference/) or [`conda`](https://docs.conda.io/projects/conda/en/latest/commands.html) to install packages, depending on how we created our virtual environment. We will walk through the commands to create virtual environments with `conda`. The environment **MUST** be activated before installing the packages for this text; otherwise, it's possible they interfere with other projects on your machine or vice versa. -# -# To install a package, we can use `conda install ` to download a package from the `default` [`conda`](https://docs.conda.io/projects/conda/en/latest/commands.html) channel. Optionally, we can provide a specific version to install `conda install pandas==0.23.4`. Even further, can define which channel that we install a package from for example we can install a package from the `conda-forge` channel by with `conda install -c conda-forge pandas=0.23.4`. Without that specification, we will get the most stable version. When we have many packages to install we will typically use a `environment.yml` or `requirements.txt` file: `conda env update -f environment.yml` from within your active environment or `conda env update -n ENVNAME -f environment.yml` if you are updating an update you are not actively in. -# -# *Note: running `conda env export ENVNAME > environment.yml` will send the list of platform-specific packages installed in the activate environment and their respective versions to the `environment.yml` file.* -# -# -# ## Classes -# -# So far we have used Python as a functional programming language, but we also have the option to use it for **object-oriented programming**. You can think of a `class` as a way to group similar functionality together. Let's create a calculator class which can handle mathematical operations for us. For this, we use the `class` keyword and define **methods** for taking actions on the calculator. These methods are functions that take `self` as the first argument. When calling them, we don't pass in anything for that argument (example after this): - - -# %% -class Calculator: - """This is the class docstring.""" - - def __init__(self): - """This is a method and it is called when we create an object of type `Calculator`.""" - self.on = False - - def turn_on(self): - """This method turns on the calculator.""" - self.on = True - - def add(self, x, y): - """Perform addition if calculator is on""" - if self.on: - return x + y - else: - print("the calculator is not on") - - -# %% [markdown] -# In order to use the calculator, we need to **instantiate** an instance or object of type `Calculator`. Since the `__init__()` method has no parameters other than `self`, we don't need to provide anything: - -# %% -my_calculator = Calculator() - -# %% [markdown] -# Let's try to add some numbers: - -# %% -my_calculator.add(1, 2) - -# %% [markdown] -# Oops!! The calculator is not on. Let's turn it on: - -# %% -my_calculator.turn_on() - -# %% [markdown] -# Let's try again: - -# %% -my_calculator.add(1, 2) - -# %% [markdown] -# We can access **attributes** on object with dot notation. In this example, the only attribute is `on`, and it is set in the `__init__()` method: - -# %% -my_calculator.on - -# %% [markdown] -# Note that we can also update attributes: - -# %% -my_calculator.on = False -my_calculator.add(1, 2) - -# %% [markdown] -# Finally, we can use `help()` to get more information on the object: - -# %% -help(my_calculator) - -# %% [markdown] -# ... and also for a method: - -# %% -help(my_calculator.add) - -# %% [markdown] -# ## Next Steps -# This was a crash course in Python. This isn't an exhaustive list of all of the features available to you. diff --git a/scripts/reading_local_files.py b/scripts/reading_local_files.py deleted file mode 100644 index f4f1cf3..0000000 --- a/scripts/reading_local_files.py +++ /dev/null @@ -1,110 +0,0 @@ -# %% [markdown] -# # An Introduction to Jupyter Notebooks -# -# Jupyter Notebooks are a file format (```*.ipynb```) that you can execute and explain your code in a step-wise format. -# > Jupyter Notebooks supports not only code execution in Python, but over 40 languages including R, Lua, Rust, and Julia with numerous [kernels](https://docs.jupyter.org/en/latest/projects/kernels.html#kernels-programming-languages). -# -# We can write in Markdown to write text with some level of control over your formatting. -# - [Here's a Link to Basic Markdown](https://www.markdownguide.org/basic-syntax/) -# - [Here's a link to Markdown's Extended Syntax](https://www.markdownguide.org/extended-syntax/) -# -# Topics We Will Cover -# - Importing different files and filetypes with [```pandas```](https://pandas.pydata.org/docs/index.html) -# - Basic Statistical Analysis of tabular data with ```pandas``` and ```numpy``` -# - Creating Charts with python packages from the [Matplotlib](https://matplotlib.org/), [Plotly](https://plotly.com/python/), or [HoloViz Ecosystem](https://holoviz.org/background.html#background-why-holoviz) -# - Evaluate the potential usecases for each visualization package -# -# ![EvidenceOfLearning](../images/learning.gif) -#
-# *This is you, enjoying the learning process.* - -# %% [markdown] -# Step 1: Import ```pandas``` into your python program. - -# %% -import pandas as pd - -# This will import the pandas and numpy packages into your Python program. - -df_json = pd.read_json("../data/food-waste-pilot/food-waste-pilot.json") -df_csv = pd.read_csv("../data/food-waste-pilot/food-waste-pilot.csv") -df_xlsx = pd.read_excel("../data/food-waste-pilot/food-waste-pilot.xlsx") - -# %% -df_csv.shape - -# %% -df_csv.head() # Grabs the top 5 items in your Dataframe by default. - -# %% -df_csv.tail() # Grabs the bottom 5 items in your Dataframe by default. - -# %% -df_csv.columns - -# %% -df_csv.dtypes # Returns the data types of your columns. - -# %% -df_csv.describe() - -# %% -df_csv.info() # Returns index, column names, a count of Non-Null values, and data types. - -# %% [markdown] -# There are multiple methods to do type conversion using pandas as well. - -# %% -# Oh no, we can see that our Collection Date is not the data type that we want, we need to convert it to a date value. - -df_csv["Collection Date"] = pd.to_datetime(df_csv["Collection Date"]) - -# %% -df_csv.info() - -# %% -# An alternative way to do this date conversion: - -df_csv["Collection Date"] = df_csv["Collection Date"].apply(pd.to_datetime) - -# %% -# astype() is more generic method to convert data types - -df_csv["Collection Date"] = df_csv["Collection Date"].astype("datetime64[ns]") - -# %% -df_csv.dtypes - -# %% -# Now that we have converted our Collection Date column to a datetime data type, we can use the dt.day_name() method to create a new column that contains the day of the week. - -df_csv["Day of Week"] = df_csv["Collection Date"].dt.day_name() - -# %% -# What if we want to know the date that we collected the most food waste? - -df_csv.loc[df_csv["Food Waste Collected"].idxmax(), ["Collection Date"]] - -# %% -# If you wanted to see our top 10 collection dates, you could do this: - -df_csv.nlargest(10, "Food Waste Collected") - -# %% -df_csv.nsmallest(10, "Food Waste Collected") - -# %% -df_csv.plot() - -# %% [markdown] -# ## You have to make sure that `pandas` parses your dates - -# %% -df_csv_parsed_dates = pd.read_csv( - "../data/food-waste-pilot/food-waste-pilot.csv", - parse_dates=True, - index_col="Collection Date", -) - -# %% -df_csv_parsed_dates.plot() diff --git a/scripts/reshaping_data.py b/scripts/reshaping_data.py deleted file mode 100644 index 3eeffeb..0000000 --- a/scripts/reshaping_data.py +++ /dev/null @@ -1,221 +0,0 @@ -# %% [markdown] -# # Reshaping Data -# -# ## About the data -# In this notebook, we will using daily temperature data from the -# [National Centers for Environmental Information (NCEI) API](https://www.ncdc.noaa.gov/cdo-web/webservices/v2). -# We will use the Global Historical Climatology Network - Daily (GHCND) dataset; -# see the documentation [here](https://www1.ncdc.noaa.gov/pub/data/cdo/documentation/GHCND_documentation.pdf). -# -# This data was collected for New York City for October 2018, using the Boonton -# 1 station (GHCND:USC00280907). It contains: -# - the daily minimum temperature (`TMIN`) -# - the daily maximum temperature (`TMAX`) -# - the daily temperature at time of observation (`TOBS`) -# -# *Note: The NCEI is part of the National Oceanic and Atmospheric Administration (NOAA) -# and, as you can see from the URL for the API, this resource was created when -# the NCEI was called the NCDC. Should the URL for this resource change in the -# future, you can search for "NCEI weather API" to find the updated one.* -# -# ## Setup -# We need to import `pandas` and read in the long format data to get started: - -# %% -import pandas as pd - -long_df = ( - pd.read_csv("../data/long_data.csv", usecols=["date", "datatype", "value"]) - .rename(columns={"value": "temp_C"}) - .assign( - date=lambda x: pd.to_datetime(x.date), temp_F=lambda x: (x.temp_C * 9 / 5) + 32 - ) -) -long_df.head() - -# %% [markdown] -# ## Transposing -# Transposing swaps the rows and the columns. We use the `T` attribute to do so: - -# %% -long_df.set_index("date").head(6).T - -# %% [markdown] -# ## Pivoting -# -# Going from long to wide format. -# -# ### `pivot()` -# -# We can restructure our data by picking a column to go in the index (`index`), -# a column whose unique values will become column names (`columns`), and the -# values to place in those columns (`values`). The `pivot()` method can be used -# when we don't need to perform any aggregation in addition to our -# restructuring (when our index is unique); if this is not the case, we need -# the `pivot_table()` method. - -# %% -pivoted_df = long_df.pivot(index="date", columns="datatype", values="temp_C") -pivoted_df.head() - -# %% [markdown] -# Now that the data is pivoted, we have wide format data that we can grab -# summary statistics with: - -# %% -pivoted_df.describe() - -# %% [markdown] -# We can also provide multiple values to pivot on, which will result in a -# hierarchical index: - -# %% -pivoted_df = long_df.pivot( - index="date", columns="datatype", values=["temp_C", "temp_F"] -) -pivoted_df.head() - -# %% [markdown] -# With the hierarchical index, if we want to select `TMIN` in Fahrenheit, we -# will first need to select `temp_F` and then `TMIN`: - -# %% -pivoted_df["temp_F"]["TMIN"].head() - -# %% [markdown] -# ### `unstack()` -# -# We have been working with a single index so far; however, we can create an -# index from any number of columns with `set_index()`. This gives us an index -# of type `MultiIndex`, where the outermost level corresponds to the first -# element in the list provided to `set_index()`: - -# %% -multi_index_df = long_df.set_index(["date", "datatype"]) -multi_index_df.head().index - -# %% [markdown] -# Notice there are now 2 index sections of the dataframe: - -# %% -multi_index_df.head() - -# %% [markdown] -# With an index of type `MultiIndex`, we can no longer use `pivot()`. We must -# now use `unstack()`, which by default moves the innermost index onto the -# columns: - -# %% -unstacked_df = multi_index_df.unstack() -unstacked_df.head() - -# %% [markdown] -# The `unstack()` method also provides the `fill_value` parameter, which let's -# us fill-in any `NaN` values that might arise from this restructuring of the -# data. Consider the case that we have data for the average temperature on -# October 1, 2018, but no other date: - -# %% -extra_data = ( - long_df.append( - [{"datatype": "TAVG", "date": "2018-10-01", "temp_C": 10, "temp_F": 50}] - ) - .set_index(["date", "datatype"]) - .sort_index() -) - -extra_data["2018-10-01":"2018-10-02"] - -# %% [markdown] -# If we use `unstack()` in this case, we will have `NaN` for the `TAVG` columns -# every day but October 1, 2018: - -# %% -extra_data.unstack().head() - -# %% [markdown] -# To address this, we can pass in an appropriate `fill_value`. However, we are -# restricted to passing in a value for this, not a strategy (like we saw with -# `fillna()`), so while `-40` is definitely not be the best value, we can use -# it to illustrate how this works, since this is the temperature at which -# Fahrenheit and Celsius are equal: - -# %% -extra_data.unstack(fill_value=-40).head() - -# %% [markdown] -# ## Melting -# Going from wide to long format. -# -# ### Setup - -# %% -wide_df = pd.read_csv("../data/wide_data.csv") -wide_df.head() - -# %% [markdown] -# ### `melt()` -# In order to go from wide format to long format, we use the `melt()` method. We have to specify: # noqa: E501 -# - `id_vars`: which column(s) uniquely identify a row in the wide format (`date`, here) -# - `value_vars`: the column(s) that contain(s) the values (`TMAX`, `TMIN`, and `TOBS`, here) # noqa: E501 -# -# Optionally, we can also provide: -# - `value_name`: what to call the column that will contain all the values once melted -# - `var_name`: what to call the column that will contain the names of the variables being measured # noqa: E501 - -# %% -melted_df = wide_df.melt( - id_vars="date", - value_vars=["TMAX", "TMIN", "TOBS"], - value_name="temp_C", - var_name="measurement", -) -melted_df.head() - -# %% [markdown] -# ### `stack()` -# Another option is `stack()`, which will pivot the columns of the dataframe -# into the innermost level of the index (resulting in an index of type -# `MultiIndex`). To illustrate this, let's set our index to be the `date` -# column: - -# %% -wide_df.set_index("date", inplace=True) -wide_df.head() - -# %% [markdown] -# By running `stack()` now, we will create a second level in our index which -# will contain the column names of our dataframe (`TMAX`, `TMIN`, `TOBS`). This -# will leave us with a `Series` object containing the values: - -# %% -stacked_series = wide_df.stack() -stacked_series.head() - -# %% [markdown] -# We can use the `to_frame()` method on our `Series` object to turn it into a -# `DataFrame` object. Since the series doesn't have a name at the moment, we -# will pass in the name as an argument: - -# %% -stacked_df = stacked_series.to_frame("values") -stacked_df.head() - -# %% [markdown] -# Once again, we have an index of type `MultiIndex`: - -# %% -stacked_df.head().index - -# %% [markdown] -# Unfortunately, we don't have a name for the `datatype` level: - -# %% -stacked_df.index.names - -# %% [markdown] -# We can use `set_names()` to address this though: - -# %% -stacked_df.index.set_names(["date", "datatype"], inplace=True) -stacked_df.index.names diff --git a/scripts/wide_vs_long.py b/scripts/wide_vs_long.py deleted file mode 100644 index ce5209a..0000000 --- a/scripts/wide_vs_long.py +++ /dev/null @@ -1,88 +0,0 @@ -# %% [markdown] # Wide vs. Long Format Data -# -# ## About the data In this notebook, we will be using daily temperature data -# from the [National Centers for Environmental Information (NCEI) -# API](https://www.ncdc.noaa.gov/cdo-web/webservices/v2). We will use the -# Global Historical Climatology Network - Daily (GHCND) dataset for the Boonton -# 1 station (GHCND:USC00280907); see the documentation -# [here](https://www1.ncdc.noaa.gov/pub/data/cdo/documentation/GHCND_documentation.pdf). -# -# *Note: The NCEI is part of the National Oceanic and Atmospheric -# Administration (NOAA) and, as you can see from the URL for the API, this -# resource was created when the NCEI was called the NCDC. Should the URL for -# this resource change in the future, you can search for "NCEI weather API" to -# find the updated one.* -# -# ## Setup - -# %% -import matplotlib.pyplot as plt -import pandas as pd - -wide_df = pd.read_csv("../data/wide_data.csv", parse_dates=["date"]) -long_df = pd.read_csv( - "../data/long_data.csv", usecols=["date", "datatype", "value"], parse_dates=["date"] -)[["date", "datatype", "value"]] # sort columns - -# %% [markdown] ## Wide format Our variables each have their own column: - -# %% -wide_df.head(6) - -# %% [markdown] Describing all the columns is easy: - -# %% -wide_df.describe(include="all") - -# %% [markdown] It's easy to graph with `pandas`: - -# %% -wide_df.plot( - x="date", - y=["TMAX", "TMIN", "TOBS"], - figsize=(15, 5), - title="Temperature in NYC in October 2018", -).set_ylabel("Temperature in Celsius") -plt.show() - -# %% [markdown] ## Long format Our variable names are now in the -# `datatype` column and their values are in the `value` column. We -# now have 3 rows for each date, since we have 3 different -# `datatypes`: - -# %% -long_df.head(6) - -# %% [markdown] Since we have many rows for the same date, using -# `describe()` is not that helpful: - -# %% -long_df.describe(include="all") - -# %% [markdown] Plotting long format data in `pandas` can be -# rather tricky. Instead we use `seaborn`: - -# %% -import seaborn as sns - -sns.set(rc={"figure.figsize": (15, 5)}, style="white") - -ax = sns.lineplot(data=long_df, x="date", y="value", hue="datatype") -ax.set_ylabel("Temperature in Celsius") -ax.set_title("Temperature in NYC in October 2018") - -plt.show() - -# %% [markdown] With long data and `seaborn`, we can easily facet -# our plots: - -# %% -sns.set(rc={"figure.figsize": (20, 10)}, style="white", font_scale=2) - -g = sns.FacetGrid(long_df, col="datatype", height=10) -g = g.map(plt.plot, "date", "value") - -g.set_titles(size=25) -g.set_xticklabels(rotation=45) - -plt.show()