diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 129542db0..38eef4aba 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -57,12 +57,17 @@ jobs: any::gridExtra local::. needs: website - - name: Setup Micromamba - uses: mamba-org/setup-micromamba@v1 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v6 with: - environment-file: environment.yml - cache-environment: false - cache-downloads: false + python-version: ${{ matrix.python-version }} + cache: "pip" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + run: | + python -m pip install --upgrade pip + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Create public directory run: | mkdir public diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7494678a0..565ddcb1d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -53,16 +53,13 @@ build-image: - name: ${DEVOPS_REGISTRY}usgs/docker:29-dind alias: docker script: - - echo ${CI_REGISTRY_PASSWORD} | docker login -u ${CI_REGISTRY_USER} --password-stdin $CI_REGISTRY + - echo "${CI_REGISTRY_PASSWORD}" | docker login -u "${CI_REGISTRY_USER}" --password-stdin $CI_REGISTRY - docker pull ${CI_REGISTRY_IMAGE}:latest || true - - cp *.tar.gz docker/ - - cd docker - - docker build - -t ${CI_REGISTRY_IMAGE}:latest - . + - docker build --cache-from ${CI_REGISTRY_IMAGE}:latest -t ${CI_REGISTRY_IMAGE}:latest -t ${CI_REGISTRY_IMAGE}:BUILD_${CI_COMMIT_SHORT_SHA} -f docker/Dockerfile . + # If this is building a git tag, also push the corresponding docker tag + - if [ -n "${CI_COMMIT_TAG}" ]; then docker tag "${CI_REGISTRY_IMAGE}:BUILD_${CI_COMMIT_SHORT_SHA}" "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}"; fi - docker push --all-tags ${CI_REGISTRY_IMAGE} - artifacts: - expire_in: 45 minutes + buildcheck: image: ${CI_REGISTRY_IMAGE}:latest @@ -135,11 +132,11 @@ longtest: pages: image: ${CI_REGISTRY_IMAGE}:latest stage: end - before_script: - - conda env create -n dataretrieval -f environment.yml dependencies: - build-image - buildcheck + variables: + RETICULATE_PYTHON: /usr/bin/python3 script: - | Rscript -e ' diff --git a/docker/Dockerfile b/docker/Dockerfile index d8997e5c7..a7e3f345f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,54 +1,60 @@ FROM code.chs.usgs.gov:5001/ctek/docker/r-lang/r-base:4.6 -# Change the name of this environment to something which pleases you, if you -# so please. But the name will not be relevant for most cases, as reticulate -# will be pointed to the environment no matter what it is named. -ARG CONDA_ENVIRONMENT_NAME=dataretrieval - -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - wget \ +# System dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3 \ + python3-pip \ + python3-dev \ + # These might already come with r-cran-sf binary, commenting out for now: + # libgdal-dev \ + # libgeos-dev \ + # libproj-dev \ && rm -rf /var/lib/apt/lists/* - -ENV CONDA_DIR="/root/conda" -ENV PATH=$CONDA_DIR/bin:$PATH -RUN wget -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" && \ - bash Miniforge3.sh -b -p "${HOME}/conda" && \ - rm Miniforge3.sh +# Python dependencies +RUN pip3 install --no-cache-dir --break-system-packages \ + dataretrieval \ + matplotlib \ + geopandas \ + numpy \ + pandas \ + scipy \ + folium>=0.12 \ + mapclassify \ + python-dateutil \ + seaborn \ + requests -# Necessary R libraries -RUN apt-get update -qq && apt-get -y --no-install-recommends install \ - r-cran-rcmdcheck \ - r-cran-testthat \ - r-cran-covr \ - r-cran-reticulate \ - r-cran-curl \ - r-cran-lubridate \ - r-cran-xml2 \ - r-cran-httr2 \ - r-cran-whisker \ - r-cran-dplyr \ - r-cran-sf \ - r-cran-data.table \ - r-cran-jsonlite \ - r-cran-readr \ - r-cran-knitr \ - r-cran-rmarkdown \ - r-cran-dt \ - r-cran-leaflet \ - r-cran-readxl \ - r-cran-pkgdown \ - r-cran-ggplot2 \ - r-cran-tidyr \ - r-cran-purrr \ - && rm -rf /var/lib/apt/lists/* +# R dependencies +RUN apt-get update -qq && apt-get install -y --no-install-recommends \ + r-cran-reticulate \ + r-cran-curl \ + r-cran-lubridate \ + r-cran-xml2 \ + r-cran-httr2 \ + r-cran-dplyr \ + r-cran-sf \ + r-cran-ggplot2 \ + r-cran-tidyr \ + r-cran-purrr \ + r-cran-jsonlite \ + r-cran-knitr \ + r-cran-rmarkdown \ + r-cran-leaflet \ + r-cran-data.table \ + r-cran-readr \ + r-cran-rcmdcheck \ + r-cran-testthat \ + r-cran-pkgdown \ + r-cran-covr \ + r-cran-readxl \ + r-cran-dt \ + && rm -rf /var/lib/apt/lists/* -ENV RETICULATE_PYTHON=/root/conda/envs/${CONDA_ENVIRONMENT_NAME}/bin/python +ENV RETICULATE_PYTHON=/usr/bin/python3 COPY *.tar.gz /tmp/pkg.tar.gz - RUN set -eux; \ - R CMD INSTALL /tmp/pkg.tar.gz && \ - Rscript -e "library(dataRetrieval)" && \ - rm -f /tmp/*.tar.gz + R CMD INSTALL /tmp/pkg.tar.gz && \ + Rscript -e "library(dataRetrieval)" && \ + rm -f /tmp/*.tar.gz diff --git a/environment.yml b/environment.yml index 057a1989d..b3b6e3e34 100644 --- a/environment.yml +++ b/environment.yml @@ -1,8 +1,14 @@ name: dataretrieval channels: - conda-forge + - defaults dependencies: + - python>=3.10 + - pip - dataretrieval - - matplotlib + - matplotlib-base - geopandas + - pyqt + - folium>=0.12 + - mapclassify prefix: /home/user/miniforge3/envs/dataretrieval diff --git a/requirements.txt b/requirements.txt index eb5711775..6e72580fb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,17 +1,7 @@ -numpy<2 -pandas==2.* -geopandas==1.1.2 -scipy -python-dateutil -requests -requests-mock -coverage -pytest -flake8 -sphinx -sphinx-rtd-theme -ipython -ipykernel -nbsphinx -nbsphinx_link +dataretrieval matplotlib +geopandas +folium +mapclassify +seaborn + diff --git a/tutorials/basic_slides_deck.qmd b/tutorials/basic_slides_deck.qmd index e814e022e..8454907df 100644 --- a/tutorials/basic_slides_deck.qmd +++ b/tutorials/basic_slides_deck.qmd @@ -11,7 +11,7 @@ format: footer: preview-links: auto title-slide-attributes: - data-background-image: hex_logo.png + data-background-image: combo_hex.png data-background-size: 15% data-background-position: 2% 2% editor: source @@ -33,11 +33,20 @@ params: library(ggplot2) library(dplyr) library(reticulate) + py_require("dataretrieval") py_require("panda") py_require("matplotlib") +py_require("seaborn") +py_require("geopandas") +py_require("folium") +py_require("mapclassify") options(dplyr.summarise.inform = FALSE) +theme_set(theme_bw(base_size = 20)) +update_geom_defaults("point", list(size = 3, color = "steelblue")) +options(ggplot2.discrete.colour = "viridis") + evaluate_python <- params$run_python dt_me <- function( @@ -72,100 +81,18 @@ dt_me <- function( ``` -## Introduction {background-image="images/hex_logo.png" background-size="15%" background-position="90% 90%" } - -In this ~90 minute introduction, the goal is: - -- Introduce the modern `dataRetrieval` workflows. - -- The intended audience is someone: - - - New to `dataRetrieval` - - - Has some R experience - -::: footer - -::: - - -## RStudio Orientation - -By default will look like: - -![](images/default_rstudio.png) - -## RStudio Appearances - -Go to Tools -> Global Options -> Appearances to change style. - -![](images/apperences.png) - -## RStudio Orientation {.smaller} - -:::: {.columns} - -::: {.column width="30%"} - -1. Create scripts. - -2. See code run. - -3. See what variables are loaded - - - Click on a data frame to View - -4. Plots and more - -::: - -::: {.column width="70%"} - -![](images/dark_mode.png) -::: - -:::: - -::: footer - -::: - - -## dataRetrieval: R-package for US water data {.smaller} - -:::: {.columns} - -::: {.column width="50%"} - -**USGS Water Data APIs * ** - -- Surface water levels - -- Groundwater levels - -- Site metadata - -- Peak flows - -- Rating curves +## Introduction {background-image="combo_flip.png" background-size="25%" background-position="95% 90%" } -- Discrete water-quality data +The goal of these slides are to: -::: +- Introduce the modern USGS water data concepts -::: {.column width="50%"} +- Introduce basic `dataretrieval` workflows (R and Python) -**Water Quality Portal (WQP) Data** +- Introduce additional topics that often come up -- Discrete water-quality data - -- USGS and non-USGS data - -::: - -:::: -## Installation +## Software Installation ::: {.panel-tabset} @@ -188,8 +115,6 @@ library(dataRetrieval) ### Python -Whether you are a user or developer we recommend installing `dataretrieval` in a virtual environment. This can be done using something like virtualenv or conda. - ```{bash} #| echo: true #| eval: false @@ -218,27 +143,15 @@ from dataretrieval import waterdata ::: -## dataRetrieval: External Documentation - -![](images/documentation_1.png){width="1000" height="500"} - -::: footer - -::: - -## dataRetrieval: External Documentation - -![](images/documentation_2.png){width="1000" height="500"} +## External Documentation -::: footer - -::: +- R package: -## dataRetrieval: External Documentation +- Python package: -![](images/documentation_3.png){width="1000" height="500"} +- WDFN Blog: -## Documentation within R: function help pages {.smaller} +## Internal Documentation {.smaller} ::: {.panel-tabset} @@ -299,62 +212,193 @@ help(waterdata.get_daily) ::: -## Exercise 1: Orientation {.smaller} +## USGS Water Data Concepts {.smaller} -::: {.panel-tabset} +:::: {.columns} -### Challenge +::: {.column width="50%"} +**USGS Water Data APIs** -1. Open RStudio +- Continuous (e.g. 15-minute sensor data) + +- Daily (e.g. mean from continuous) + +- Monitoring Location Information -2. Install `dataRetrieval`, `dplyr`, `ggplot2`, and `data.table` (if they are not already installed). +- Time Series Information -3. Load `dataRetrieval` +- Latest Daily/Continuous -4. Open the help file for the function `read_waterdata_daily` +- Field Measurements + +::: + +::: {.column width="50%"} +**USGS Water Data APIs** + +- Peak Flows + +- Rating Curves + +- Discrete Water-Quality + +**Water Quality Portal (WQP) Data** + +- Discrete water-quality data (USGS & others) + +::: + +:::: + +::: {.callout-important} +## Each of these is has a different: + +* API endpoint +* `dataRetrieval` function +* Output format +::: + + +::: footer + +::: + +## USGS Basic Retrievals {.smaller} + +The USGS uses various codes for basic retrievals. These codes can have leading zeros, therefore they need to be a character surrounded in quotes ("00060"). + +* Site ID (often 8 or 15-digits) +* Parameter Code (5 digits) + + Full list: `read_metadata("parameter-codes")` +* Statistic Code (for daily values) + + Full list: `read_metadata("statistic-codes")` + +## USGS Basic Retrievals Parameter and Statistic Codes + +Here are some examples of a few common codes: + + +```{r echo=FALSE, eval=TRUE} +library(knitr) + +df <- data.frame( + pCode = c("00060", "00065", "00010", "00400"), + shName = c("Discharge", "Gage Height", "Temperature", "pH") +) + +names(df) <- c("Parameter Code", "Short Name") + +df2 <- data.frame( + pCode = c("00001", "00002", "00003", "00008"), + shName = c("Maximum", "Minimum", "Mean", "Median") +) + +names(df2) <- c("Statistic Code", "Short Name") + +knitr::kable(list(df, df2)) +``` -5. Navigate to and find the list of function help files and explore some articles in "Additional Articles" +## `dataRetrieval` can help! + +::: {.panel-tabset} +### R -### Solution: +```{r} +#| eval: false +parameter_codes <- read_waterdata_metadata("parameter-codes") +statistic_codes <- read_waterdata_metadata("statistic-codes") +# Others: +agency_codes <- read_waterdata_metadata("agency-codes") +aquifer_codes <- read_waterdata_metadata("aquifer-codes") +aquifer_types <- read_waterdata_metadata("aquifer-types") +coordinate_datum_codes <- read_waterdata_metadata("coordinate-datum-codes") +huc_codes <- read_waterdata_metadata("hydrologic-unit-codes") +national_aquifer_codes <- read_waterdata_metadata("national-aquifer-codes") +reliability_codes <- read_waterdata_metadata("reliability-codes") +site_types <- read_waterdata_metadata("site-types") +topographic_codes <- read_waterdata_metadata("topographic-codes") +time_zone_codes <- read_waterdata_metadata("time-zone-codes") +counties <- read_waterdata_metadata("counties") +states <- read_waterdata_metadata("states") +``` +### Python -```{r fig.height=7} +```{python} #| eval: false -install.packages(c("dataRetrieval", "dplyr", "ggplot2", "data.table")) -library(dataRetrieval) -?read_waterdata_daily +parameter_codes = waterdata.get_reference_table("parameter-codes") +statistic_codes = waterdata.get_reference_table("statistic-codes") +# Others: +agency_codes = waterdata.get_reference_table("agency-codes") +aquifer_codes = waterdata.get_reference_table("aquifer-codes") +aquifer_types = waterdata.get_reference_table("aquifer-types") +coordinate_datum_codes = waterdata.get_reference_table("coordinate-datum-codes") +huc_codes = waterdata.get_reference_table("hydrologic-unit-codes") +national_aquifer_codes = waterdata.get_reference_table("national-aquifer-codes") +reliability_codes = waterdata.get_reference_table("reliability-codes") +site_types = waterdata.get_reference_tablea("site-types") +topographic_codes = waterdata.get_reference_table("topographic-codes") +time_zone_codes = waterdata.get_reference_table("time-zone-codes") +counties = waterdata.get_reference_table("counties") +states = waterdata.get_reference_table("states") ``` +::: {style="font-size: 50%;"} +Each function returns a Tuple, containing a dataframe and a Metadata class. +::: + ::: ::: footer ::: -## dataRetrieval Updates {background-image="images/hex_logo.png" background-size="15%" background-position="85% 80%" } -Are you a seasoned `dataRetrieval` user? -Here are resources for recent major changes: +## Exercise 1: Orientation {.smaller} + +::: {.panel-tabset} + +### Challenge + +1. Open your preferred IDE (RStudio, VSCode, PyCharm, etc) or Jupyter notebook + +2. R: Install `dataRetrieval`, `ggplot2`, `dplyr`, `leaflet` (if you haven't already) + +2. Python: Install `dataretrieval`, `matplotlib`, `geopandas`, `folium`, `seaborn` (if you haven't already) + +3. Load `dataRetrieval` (R) / waterdata module in `dataretrieval` (Python) + +4. Open the help file for the function `read_waterdata_daily` -* [Changes to dataRetrieval](https://doi-usgs.github.io/dataRetrieval/articles/changes_slides.html) -* [Water Data API Introduction](https://doi-usgs.github.io/dataRetrieval/articles/read_waterdata_functions.html) +### R -* [Samples Data Introduction](https://doi-usgs.github.io/dataRetrieval/articles/samples_data.html) +```{r} +#| eval: false +install.packages(c("dataRetrieval", "ggplot2", "leaflet", "dplyr")) +library(dataRetrieval) +?read_waterdata_daily +``` -## What's New? {.smaller} +### Python -There's been a lot of changes to `dataRetrieval` over the past year. If you'd like to see an overview of those changes, visit: [Changes to dataRetrieval](https://doi-usgs.github.io/dataRetrieval/articles/changes_slides.html) +```{bash} +#| echo: true +#| eval: false +pip install dataretrieval matplotlib geopandas folium seaborn +``` -Biggest changes: -* NWIS servers will be shut down, so all `readNWIS` functions will eventually stop working +```{python} +#| eval: false +from dataretrieval import waterdata -* `read_waterdata` functions are modern and should be used when possible +help(waterdata.get_daily) +``` -* The "USGS Water Data APIs" are the new home for USGS data +::: ::: footer @@ -377,18 +421,11 @@ Biggest changes: 1. Request a USGS Water Data API Token: -2. Save it in a safe place (KeePass or other password management tool) - -3. Add it to your .Renviorn file as API_USGS_PAT. +2. Save it in a safe place (KeyPass or other password management tool) -4. Restart R - -5. Check that it worked by running (you should see your token printed in the Console): +3. Add it as environment variable -```{r} -#| eval: false -Sys.getenv("API_USGS_PAT") -``` +4. Restart See next slide for a demonstration. @@ -396,9 +433,13 @@ See next slide for a demonstration. ::: -## USGS Water Data API Token: Example {.smaller} +## Water Data API Token: Example {.smaller} -My favorite method to do add your token to .Renviron is to use the `usethis` package. Let's pretend the token sent you was "abc123": +Let's pretend the token sent you was "abc123" + +::: {.panel-tabset} + +### R 1. Run in R: ```{r} @@ -409,209 +450,124 @@ usethis::edit_r_environ() 2. Add this line to the file that opens up: -```{r} -#| eval: false +``` API_USGS_PAT = "abc123" ``` -3. Save that file using the save button +3. Save that file 4. Restart R/RStudio. -5. Run after restarting R: +5. Check that it worked by running (you should see your token printed in the Console): ```{r} #| eval: false Sys.getenv("API_USGS_PAT") ``` -## USGS Water Data API Token: Example {.smaller .nostretch} - -![](images/save_token.png){width="50%"} - -After save and restart, check that it worked by running: +::: {.callout-note collapse="true"} +Your .Renviorn file should never be pushed to a public repository. +::: -```{r} -#| eval: false -Sys.getenv("API_USGS_PAT") -``` +### Python: Project -::: footer +1. Create a file in your working directory .env -::: +2. Add this line to the .env file: -## USGS Basic Retrievals {.smaller} +``` +API_USGS_PAT = "abc123" +``` +4. Restart your python session -The USGS uses various codes for basic retrievals. These codes can have leading zeros, therefore they need to be a character surrounded in quotes ("00060"). +5. Check that it worked by running (you should see your token printed in the Console): -* Site ID (often 8 or 15-digits) -* Parameter Code (5 digits) - + Full list: `read_waterdata_parameter_codes()` -* Statistic Code (for daily values) - + Full list: `read_metadata("statistic-codes")` +```{python} +#| echo: true +#| eval: false +import os +os.getenv("API_USGS_PAT") +'abc123' +``` -## USGS Basic Retrievals Parameter and Statistic Codes +::: {.callout-note collapse="true"} +Your .env file should never be pushed to a public repository. +::: -Here are some examples of a few common codes: +### Python: Conda +1. Open Miniforge, Anaconda, etc. -```{r echo=FALSE, eval=TRUE} -library(knitr) +2. Activate enviornment -df <- data.frame( - pCode = c("00060", "00065", "00010", "00400"), - shName = c("Discharge", "Gage Height", "Temperature", "pH") -) +```{bash} +#| eval: false +conda activate flow_bootcamp +``` -names(df) <- c("Parameter Code", "Short Name") +3. Add variable: -df2 <- data.frame( - pCode = c("00001", "00002", "00003", "00008"), - shName = c("Maximum", "Minimum", "Mean", "Median") -) +```{bash} +#| eval: false +conda env config vars set API_USGS_PAT="abc123" +``` -names(df2) <- c("Statistic Code", "Short Name") +4. Reactivate enviornment -knitr::kable(list(df, df2)) +```{bash} +#| eval: false +conda activate flow_bootcamp ``` +5. Open your enviornment (for example Jupyter), and test that it's there: +```{python} +#| eval: false +import os -## Let's Go! {.smaller} - -We're going walk through 3 retrievals: +print(os.getenv("API_USGS_PAT")) +"abc123" +``` -* **Workflow 1**: Daily Data - - Uses the new USGS Water Data API - - - Modern data access point going forward +### Python: Hard-coded -* **Workflow 2**: Discrete Data +Within your code, add: - - Uses new USGS Samples Data - - - Modern data access point going forward - -* **Workflow 3**: Join Daily and Discrete +```{python} +#| eval: false +import os +os.environ["API_USGS_PAT"] = "abc123" +``` -* **Workflow 4**: Continuous Data - - - Uses the new USGS Water Data API - - - Modern data access point going forward +This is not ideal because you are hard-coding your personal access token in the script/notebook. You would not want to share this code on a public repository for example. -* **Workflow 5**: Join Continuous and Discrete +::: ::: footer ::: -## Workflow 1: Daily data for known site {.smaller} +## Water Data APIs: Initial Tips -Let's pull daily mean discharge data for site "USGS-0940550", getting all the data from the last year. +Use your "tab" key! ::: {.panel-tabset} ### R -```{r} -#| message: true -library(dataRetrieval) -site <- "USGS-09405500" -pcode <- "00060" # Discharge -stat_cd <- "00003" # Mean - -df <- read_waterdata_daily( - monitoring_location_id = site, - parameter_code = pcode, - statistic_id = stat_cd, - time = "P365D" -) -nrow(df) -``` +![](images/autocomplete.png) ### Python -```{python} -#| eval: !expr evaluate_python -from dataretrieval import waterdata +![](images/autocomplete_python.png){width="60%"} -site = "USGS-09405500" -pcode = "00060" # Discharge -stat_cd = "00003" # Mean -df, md = waterdata.get_daily( - monitoring_location_id=site, - parameter_code=pcode, - statistic_id=stat_cd, - time="P365D", -) - -df.shape[0] -``` - -::: - -::: footer - -::: - -## Workflow 1: Look at Daily Data {.smaller} - -In RStudio, click on the data frame in the upper right Environment tab to open a Viewer. - -```{r} -#| echo: false -dt_me( - df |> - sf::st_drop_geometry(), - page_length = 3 -) -``` - -::: footer - -::: - -## Workflow 1: Plot Daily Data - -::: {.panel-tabset} - -### R - -Let's use `ggplot2` to visualize the data. - -```{r} -#| echo: true -#| output-location: column -library(ggplot2) -theme_set(theme_bw(base_size = 24)) -update_geom_defaults("point", list(size = 3, color = "steelblue")) -options(ggplot2.discrete.colour = "viridis") - -ggplot(data = df) + - geom_point(aes(x = time, y = value, color = approval_status)) -``` +### Jupyter Lab -### Python - -Let's use `matplotlib` to visualize the data. - -```{python} -#| echo: true -#| output-location: column -import matplotlib.pyplot as plt -import pandas as pd - -plt.rcParams["font.size"] = 20 - -levels, categories = pd.factorize(df["approval_status"]) +Shift + Tab: -fig, ax = plt.subplots() -scatter = ax.scatter(x=df.time, y=df.value, c=levels) -fig.legend(scatter.legend_elements()[0], categories, title="Status") -``` +![](images/jupyter_tab.png){width="60%"} ::: @@ -619,15 +575,9 @@ fig.legend(scatter.legend_elements()[0], categories, title="Status") ::: -## Water Data API Notes: Argument input +## Water Data API Notes: Arguments -Use your "tab" key! - -![](images/autocomplete.png) - -## Water Data API Notes: Arguments {.smaller} - -* When you look at the help file for the new functions, you’ll notice there are lots of possible inputs (arguments). +* When you look at the help file for the new functions, you’ll notice there are lots of possible inputs parameters. * You **DO NOT** need to (and should not!) specify **all** of these parameters. @@ -644,7 +594,7 @@ discharge <- read_waterdata_daily( ::: {.fragment} -::: {style="font-size: 75%;"} +::: {style="font-size: 50%;"} Since no list of sites or bounding box was defined, **ALL** the daily data in **ALL** the country with parameter code "00060" and statistic code "00003" will be returned. @@ -663,7 +613,7 @@ Since no list of sites or bounding box was defined, **ALL** the daily data in ** ::: {.column width="50%"} -The "time" argument has a few options: +Time parameters have a few options: * A single date (or date-time): "2024-10-01" or "2024-10-01T23:20:50Z" @@ -679,6 +629,10 @@ The "time" argument has a few options: Here are a bunch of valid inputs: +::: {.panel-tabset} + +### R + ```{r} #| code-line-numbers: "1-9|10-11|12-15|16-19" # Ask for exact times: @@ -702,215 +656,119 @@ time = "P7D" # past 7 days time = "PT12H" # past hours ``` -::: - -:::: - -## Workflow 2: Discrete data for known site - -Use your "tab" key! - -![](images/autocomplete_samples.png) - -## Workflow 2: Discrete data for known site {.smaller} - -Let's get orthophosphate ("00660") data from the Shenandoah River at Front Royal, VA ("USGS-01631000"). - -::: {.panel-tabset} - -### R - -```{r} -#| message: true -site <- "USGS-01631000" -pcode <- "00660" - -qw_data <- read_waterdata_samples( - monitoringLocationIdentifier = site, - usgsPCode = pcode, - dataType = "results", - dataProfile = "basicphyschem" -) -ncol(qw_data) -``` - ### Python ```{python} -#| eval: !expr evaluate_python -site = "USGS-01631000" -pcode = "00660" - -qw_data, md_qw = waterdata.get_samples( - monitoringLocationIdentifier = site, - usgsPCode = pcode, - service = "results", - profile = "basicphyschem", -) - -qw_data.shape[1] +# Ask for exact times: +time = "2025-01-01" +# Ask for specific range +time = "2025-01-01/2026-01-01" +# Asking beginning of record to specific end: +time = "../2024-01-01" # or Date or POSIX +# Asking specific beginning to end of record: +time = "2024-01-01/.." # or Date or POSIX +# Ask for period +time = "P1M" # past month +time = "P7D" # past 7 days +time = "PT12H" # past hours ``` -::: - -That's a LOT of columns returned. We won't look at them here, but you can use `View` in RStudio to explore on your own. -::: footer +::: ::: -## USGS Samples Data Notes: Data Types and Profiles +:::: -* There are 2 arguments that dictate what kind of data is returned - - "dataType" defines what kind of data comes back - - "dataProfile" defines what columns from that type come back +## Let's Go! -## Data Types and Profiles {.smaller} +* **Workflow 1**: Find Available Sites -```{r} -#| echo: false -df <- tibble( - dataType = c( - "results", - "locations", - "activities", - "projects", - "organizations" - ), - Description = c( - "Results data and metadata for measures and observations matching your query", - "Find monitoring locations that have data matching your query", - "Information about the monitoring activities conducted that produced data", - "Information on the projects that have results matching your data query", - "Information about the organizations that have provided data that matches your query" - ), - dataProfile = c( - 'fullphyschem
basicphyschem
fullbio
basicbio
narrow
resultdetectionquantitationlimit
labsampleprep
count', - 'site
count', - 'sampact
actmetric
actgroup
ncount', - 'project
projectmonitoringlocationweight', - 'organization
count' - ) -) +* **Workflow 2**: Find Available Data -dt_me(df, escape = FALSE, paging = FALSE) -``` +* Challenge 1 + +* **Workflow 3**: Get Latest Data -::: footer +* **Workflow 4**: Get All Data -::: +* Challenge 2 + +* **Workflow 5**: Discrete Water Quality -## Workflow 2: Discrete data censoring {.smaller} +## Workflow 1: Find Available Sites -Let's pull a few columns out and look at those: +Let's get all the monitoring locations for Dane County, Wisconsin: ::: {.panel-tabset} ### R ```{r} -library(dplyr) - -qw_data_slim <- qw_data |> - select( - Date = Activity_StartDate, - Result_Measure, - DL_cond = Result_ResultDetectionCondition, - DL_val = DetectionLimit_MeasureA, - DL_type = DetectionLimit_TypeA - ) |> - mutate( - Result = if_else(DL_cond != "", DL_val, Result_Measure), - Detected = if_else(DL_cond != "", "Not Detected", "Detected") - ) |> - arrange(Detected) +#| message: false +site_info <- read_waterdata_monitoring_location( + state_name = "Wisconsin", + county_name = "Dane County" +) ``` -* What is `|>`? It's a pipe! It says take 'this thing' and put it in 'that thing'. You'll also see `%>%` in code, it is also a pipe - they are basically the same. - ### Python ```{python} #| eval: !expr evaluate_python -import numpy as np - -qw_data_slim = ( - qw_data.rename( - columns={ - "Activity_StartDate": "Date", - "Result_ResultDetectionCondition": "DL_cond", - "DetectionLimit_MeasureA": "DL_val", - "DetectionLimit_TypeA": "DL_type", - } - )[["Date", "Result_Measure", "DL_cond", "DL_val", "DL_type"]] - .assign( - Result=lambda x: np.where( - x["DL_cond"].notna(), x["DL_val"], x["Result_Measure"] - ) - ) - .assign( - Detected=lambda x: np.where(x["DL_cond"].notna(), "Not Detected", "Detected") - ) - .sort_values(by="Detected", ascending=False) + +site_info, md = waterdata.get_monitoring_locations( + state_name="Wisconsin", county_name="Dane County" ) ``` ::: + +::: {.callout-note collapse="true"} +## Note on county names +`read_waterdata_monitoring_location` requires "County" in the county_name argument. You can check county names using: +```{r} +#| eval: false +counties <- check_waterdata_sample_params(service = "counties") +``` +::: + ::: footer ::: -## Workflow 2: Discrete data censoring information {.smaller} +## site_info {.smaller .scrollable} ```{r} #| echo: false -dt_me(qw_data_slim, page_length = 8, font = "0.7em") +dt_me(site_info, 5, "0.6em") ``` ::: footer ::: -## Workflow 3: Join Discrete and Daily - -* One common workflow is to join discrete data with daily data. - -* In this example, we will look at a site that measures both water quality parameters and has daily mean discharge. - -* We will use the `dplyr::left_join` to join the 2 data frames by a date. - -::: footer - -::: +## site_info_refined {.smaller} -## Step 1: Get the data {.smaller} +Now that we've seen the whole data set, maybe we realize in the future we can ask for just stream sites, and we only really need a few of those columns: ::: {.panel-tabset} ### R ```{r} -site <- "USGS-04183500" -p_code_dv <- "00060" -stat_cd <- "00003" -p_code_qw <- "00665" -start_date <- "2015-07-03" -end_date <- "2025-07-03" - -qw_data <- read_waterdata_samples( - monitoringLocationIdentifier = site, - usgsPCode = p_code_qw, - activityStartDateLower = start_date, - activityStartDateUpper = end_date, - dataProfile = "basicphyschem" -) - -dv_data <- read_waterdata_daily( - monitoring_location_id = site, - parameter_code = p_code_dv, - statistic_id = stat_cd, - time = c(start_date, end_date) +#| message: true +site_info_refined <- read_waterdata_monitoring_location( + state_name = "Wisconsin", + county_name = "Dane County", + site_type = "Stream", + properties = c( + "monitoring_location_id", + "monitoring_location_name", + "drainage_area", + "geometry" + ) ) ``` @@ -918,175 +776,204 @@ dv_data <- read_waterdata_daily( ```{python} #| eval: !expr evaluate_python -site = "USGS-04183500" -p_code_dv = "00060" -stat_cd = "00003" -p_code_qw = "00665" -start_date = "2015-07-03" -end_date = "2025-07-03" - -qw_data, md_qw = waterdata.get_samples( - monitoringLocationIdentifier=site, - usgsPCode=p_code_qw, - activityStartDateLower=start_date, - activityStartDateUpper=end_date, - profile="basicphyschem", -) - -dv_data, md_dv = waterdata.get_daily( - monitoring_location_id=site, - parameter_code=p_code_dv, - statistic_id=stat_cd, - time=(start_date + "/" + end_date), +site_info_refined, md = waterdata.get_monitoring_locations( + state_name="Wisconsin", + county_name="Dane County", + site_type="Stream", + properties=[ + "monitoring_location_id", + "monitoring_location_name", + "drainage_area", + "geometry", + ], ) ``` - ::: ::: footer ::: -## Step 2: Join +## Map with geometry {.smaller} ::: {.panel-tabset} -### R +### R: ggplot2 ```{r} -little_dv <- dv_data |> - select(time, Flow = value, monitoring_location_id) +#| output-location: default +library(ggplot2) -qw_data_joined <- qw_data |> - left_join(little_dv, by = c("Activity_StartDate" = "time")) +ggplot(data = site_info_refined) + + geom_sf() ``` - -### Python +### Python: matplotlib ```{python} #| eval: !expr evaluate_python -little_dv = dv_data.rename(columns={"value": "Flow"})[ - ["time", "Flow", "monitoring_location_id"] -] +import matplotlib.pyplot as plt +import geopandas as gpd -qw_data["Activity_StartDate"] = pd.to_datetime( - qw_data["Activity_StartDate"], format="%Y-%m-%d" -) +site_info_refined.plot() -qw_data_joined = pd.merge( - qw_data, little_dv, left_on="Activity_StartDate", right_on="time", how="left" -) ``` ::: -* "Activity_StartDate" (on the left side data frame) and "time" (on the right side data frame) need to be the same type (in this case, both are Date objects). - ::: footer ::: -## Step 2: Join (cont.) - -* You could join on multiple columns: +## Interactive Map ::: {.panel-tabset} ### R ```{r} -#| eval: false -qw_data <- qw_data |> - left_join( - little_dv, - by = c( - "Activity_StartDate" = "time", - "Location_Identifier" = "monitoring_location_id" - ) - ) +#| eval: true +#| output-location: slide +library(leaflet) +#default leaflet crs: +leaflet_crs <- "+proj=longlat +datum=WGS84" + +leaflet( + data = site_info_refined |> + sf::st_transform(crs = leaflet_crs) +) |> + addProviderTiles("CartoDB.Positron") |> + addCircleMarkers(popup = ~monitoring_location_name, radius = 3, opacity = 1) ``` -See `dplyr` documentation for lots of joining options, but I find `left_join` my "go-to" for straightforward joins. - ### Python +If you have `geopandas` installed, the function will return a GeoDataFrame with a geometry column containing the monitoring locations’ coordinates. You can use `gpd.explore()` to view your geometry coordinates on an interactive map. + ```{python} -#| eval: !expr evaluate_python -qw_data = pd.merge( - qw_data, - little_dv, - left_on=["Activity_StartDate", "Location_Identifier"], - right_on=["time", "monitoring_location_id"], - how="left", +#| eval: false +site_info_refined.set_crs(crs="WGS84").explore( + marker_kwds=dict(radius=7), + style_kwds=dict(opacity=1, fillOpacity=1), + tiles="CartoDB.Positron", ) ``` ::: ::: footer - + ::: -## Step 3: Inspect +## Workflow 2: Find Available Data -Let's take a quick peak: +Let's get all the time series in Dane County, WI with daily mean (statistic_id = "00003") discharge (parameter code = "00060") or temperature (parameter code = "00010"). ::: {.panel-tabset} ### R ```{r} -#| output-location: column -ggplot(data = qw_data_joined) + - geom_point(aes(x = Flow, y = Result_Measure)) +sites_available <- read_waterdata_combined_meta( + state_name = "Wisconsin", + county_name = "Dane County", + parameter_code = c("00060", "00010"), + statistic_id = c("00003") +) ``` ### Python ```{python} -#| eval: !expr evaluate_python -#| output-location: column -plt.figure() -plt.scatter(x=qw_data_joined.Flow, y=qw_data_joined.Result_Measure) -``` +sites_available, md = waterdata.get_combined_metadata( + state_name = "Wisconsin", + county_name = "Dane County", + parameter_code = ["00060", "00010"], + statistic_id = "00003" +) +``` ::: -## Exercise 2: Joins {.smaller} +## sites_available {.smaller .scrollable} + +Selecting just a few columns: + +```{r} +#| echo: false +dt_me( + sites_available |> + sf::st_drop_geometry() |> + filter(!is.na(begin)) |> # public, but "grade" set to unusable + select(monitoring_location_id, parameter_name, parameter_code, begin, end), + 6, + "0.7em" +) +``` + +## Challenge 1 {.smaller} ::: {.panel-tabset} -### Challenge +### Problem Statement -`dplyr` comes with some data sets. To look at them run: +1. How many USGS stream sites are within Tuscaloosa County, Alabama? -```{r} -library(dplyr) -band_members <- band_members -band_instruments <- band_instruments -``` +2. What are the unique parameter_names that come back from USGS sites in Tuscaloosa County, Alabama? -1. Run that code and view the 2 data frames to see what they look like. +3. What site has the longest period of record for daily mean discharge? -2. Join the instruments to the "band_members" by name. +4. Bonus: Create an interactive map of all sites that measure gage height. -3. Join the members to the "band_instruments" by name. +The amount you get done during this break will highly depend on the extent of your coding background. Use this time to explore dataretrieval functions and outputs. -### Solution: +### Solution +When these slides were generated on `{r} Sys.Date()`, the results were: ```{r} -band_members |> - left_join(band_instruments, by = "name") - -band_instruments |> - left_join(band_members, by = "name") +#| message: false +#| echo: false +library(dplyr) +cat("1. ") +tus <- read_waterdata_combined_meta( + state_name = "Alabama", + county_name = "Tuscaloosa County", + site_type = "Stream" +) +tus |> + filter(parameter_name == "Gage height") |> + select(monitoring_location_id) |> + distinct() |> + pull() |> + length() + +cat("2. ") +unique(tus$parameter_name) + +cat("3. ") +tus_summary <- tus |> + filter(parameter_name == "Discharge", data_type == "Daily values") |> + mutate(por = end - begin) |> + arrange(desc(por)) +tus_summary$monitoring_location_name[1] +tus_summary$begin[1] +tus_summary$end[1] ``` +### Bonus +```{r} +#| echo: false +leaflet( + data = tus_summary |> + sf::st_transform(crs = leaflet_crs) +) |> + addProviderTiles("CartoDB.Positron") |> + addCircleMarkers(popup = ~monitoring_location_name, radius = 3, opacity = 1) +``` ::: @@ -1094,325 +981,317 @@ band_instruments |> ::: -## Workflow 4: Continuous data for known site +## Workflow 3: Get Latest Data {.smaller} -* Continuous data is the high-frequency sensor data. - -* We'll look at Suisun Bay a Van Sickle Island NR Pittsburg CA ("USGS-11455508"), with parameter code "99133" which is Nitrate plus Nitrite. - -## Workflow 4: Continuous data for known site {.smaller} +Let's get the daily discharge measurements in Dane County, WI (parameter code = "00060") that has measured data within the last 14 days. ::: {.panel-tabset} ### R -:::: {.columns} - -::: {.column width="65%"} - ```{r} -#| eval: false -site_id <- "USGS-11455508" -p_code_rt <- "99133" -start_date <- "2024-01-01" -end_date <- "2024-06-01" - -continuous_data <- read_waterdata_continuous( - monitoring_location_id = site_id, - parameter_code = p_code_rt, - time = c(start_date, end_date) +latest_sites <- read_waterdata_combined_meta( + state_name = "Wisconsin", + county_name = "Dane County", + parameter_code = c("00060"), + last_modified = "P14D", + data_type = "Continuous values" ) -names(continuous_data) -``` - -::: -::: {.column width="35%"} - -```{r} -#| results: markup -#| echo: false -options(width = 30) -site_id <- "USGS-11455508" -p_code_rt <- "99133" -start_date <- "2024-01-01" -end_date <- "2024-06-01" - -continuous_data <- read_waterdata_continuous( - monitoring_location_id = site_id, - parameter_code = p_code_rt, - time = c(start_date, end_date) +latest_discharge <- read_waterdata_latest_continuous( + monitoring_location_id = latest_sites$monitoring_location_id, + parameter_code = "00060" ) -names(continuous_data) ``` -::: - -:::: - ### Python -:::: {.columns} - -::: {.column width="65%"} - ```{python} -#| eval: false -site_id = "USGS-11455508" -p_code_rt = "99133" -date_range = "2024-01-01/2024-06-01" - -continuous_data, md_cont = waterdata.get_continuous( - monitoring_location_id=site_id, parameter_code=p_code_rt, time=date_range +latest_sites, md = waterdata.get_combined_metadata( + state_name = "Wisconsin", + county_name = "Dane County", + parameter_code = "00060", + last_modified = "P14D", + data_type = "Continuous values" ) -``` - -::: - -::: {.column width="35%"} - -```{python} -#| eval: !expr evaluate_python -#| results: markup -#| echo: false -pd.set_option("display.width", 30) -site_id = "USGS-11455508" -p_code_rt = "99133" -date_range = "2024-01-01/2024-06-01" -continuous_data, md_cont = waterdata.get_continuous( - monitoring_location_id=site_id, parameter_code=p_code_rt, time=date_range +latest_discharge, md = waterdata.get_latest_continuous( + monitoring_location_id = latest_sites.monitoring_location_id, + parameter_code = "00060" ) -continuous_data.columns ``` ::: -:::: +::: footer ::: - -## Workflow 4: Inspect +## Workflow 3: Get Latest Data {.smaller} ::: {.panel-tabset} ### R ```{r} -#| output-location: column -ggplot(data = continuous_data) + - geom_point(aes(x = time, y = value)) +#| eval: false +pal <- colorNumeric("viridis", latest_discharge$value) + +leaflet( + data = latest_discharge |> + sf::st_transform(crs = leaflet_crs) +) |> + addProviderTiles("CartoDB.Positron") |> + addCircleMarkers( + popup = paste( + latest_discharge$monitoring_location_id, + "
", + latest_discharge$time, + "
", + latest_discharge$value, + latest_discharge$unit_of_measure + ), + color = ~ pal(value), + radius = 3, + opacity = 1 + ) |> + addLegend( + pal = pal, + position = "bottomleft", + title = "Latest Discharge", + values = ~value + ) ``` ### Python ```{python} -#| eval: !expr evaluate_python -#| output-location: column -plt.figure() -plt.scatter(x=continuous_data.time, y=continuous_data.value) +#| eval: true +#| output-location: slide + +latest_discharge.set_crs(crs="WGS84").explore( + marker_kwds=dict(radius=7), + style_kwds=dict(opacity=1, fillOpacity=1), + tiles="CartoDB.Positron", + column="value", + cmap="viridis", + zoom_start=10, +) ``` ::: -## Workflow 5: Join Discrete and Continuous +::: footer + +::: -That same site also measures discrete Nitrate plus Nitrite, which is parameter code "00631". Let's first grab that data: + +## Workflow 4: Get All Data + +Let's get daily discharge data for the last 3 years from 2 sites: ::: {.panel-tabset} ### R ```{r} -#| message: true -discrete_data <- read_waterdata_samples( - monitoringLocationIdentifier = "USGS-11455508", - usgsPCode = "00631", - activityStartDateLower = start_date, - activityStartDateUpper = end_date, - dataProfile = "basicphyschem" +daily <- read_waterdata_daily( + monitoring_location_id = c("USGS-05406457", "USGS-05427930"), + parameter_code = c("00060"), + statistic_id = "00003", + time = c("2022-10-01", "2025-10-01") ) ``` ### Python ```{python} -#| eval: !expr evaluate_python -discrete_data, md_qw = waterdata.get_samples( - monitoringLocationIdentifier = "USGS-11455508", - usgsPCode = "00631", - activityStartDateLower = "2024-01-01", - activityStartDateUpper = "2024-06-01", - profile = "basicphyschem" +df, md = waterdata.get_daily( + monitoring_location_id= ["USGS-05406457", "USGS-05427930"], + parameter_code="00060", + statistic_id="00003", + time="2022-10-01/2025-10-01", ) ``` ::: -## Workflow 5: Join Discrete and Continuous - -* We now want to join the **closest** continuous sensor time with the discrete sample time. - -* This is trickier than joining by exact matches. - -* `dplyr` has a way, but it's complicated if you want the absolute closest in either direction - -* Another package `data.table` has a slick way to get the closest matches - -## Workflow 5: Join Discrete and Continuous +## Workflow 4: Get All Data: Plot It {.smaller} ::: {.panel-tabset} ### R ```{r} -#| code-line-numbers: "1|2-3|5-10" -library(data.table) -setDT(discrete_data)[, join_date := Activity_StartDateTime] -setDT(continuous_data)[, join_date := time] - -closest_dt <- continuous_data[ - discrete_data, - on = .(join_date), - roll = "nearest" -] -closest_dt <- data.frame(closest_dt) +ggplot(data = daily) + + geom_line(aes(x = time, y = value, color = approval_status)) + + facet_grid(monitoring_location_id ~ ., ) +``` + +### Python + +```{python} +#| eval: true +import matplotlib.pyplot as plt +import pandas as pd +import seaborn + +levels, categories = pd.factorize(df["approval_status"]) +graph = seaborn.FacetGrid( + df, row="monitoring_location_id", hue="approval_status", height=3, aspect=5 +) +graph.map(plt.scatter, "time", "value").add_legend() ``` ::: ::: footer - + ::: -## Workflow 5: Inspect +## Challenge 2 -```{r} -#| output-location: column -ggplot(data = closest_dt) + - geom_point(aes(x = Result_Measure, y = value)) + - geom_abline() + - expand_limits(x = 0, y = 0) + - xlab("Discrete") + - ylab("Continuous") -``` +### Problem +1. Navigate to the National Water Dashboard -## Data Discovery +2. Zoom in and explore sections of the map that have generally higher than normal streamflow. -The process for discovering data is a bit in flux with NWIS retiring. I expect a new process will be introduced soon. For now here are some options. +3. Zoom in and explore sections of the map that have generally lower than normal streamflow. -1. `read_waterdata_ts_meta` discovers daily and continuous time series +4. Pick a USGS site that is interesting to you (maybe you are a kayaker, maybe you fish, maybe it's a local stream, maybe it's in an extreme flood/drought). -2. `summarize_waterdata_samples` discovers discrete data at specific monitoring locations +5. Plot the daily mean discharge data for all time for the site you picked. -The next slides will demo how to use those. -## Data Discovery: Time Series {.smaller} +## Workflow 5: Get Discrete Water Quality Data {.smaller} + +Let's get orthophosphate ("00660") data from the Shenandoah River at Front Royal, VA ("USGS-01631000"). ::: {.panel-tabset} ### R + ```{r} -ts_available <- read_waterdata_ts_meta(monitoring_location_id = "USGS-04183500") +#| message: true +site <- "USGS-01631000" +pcode <- "00660" + +qw_data <- read_waterdata_samples( + monitoringLocationIdentifier = site, + usgsPCode = pcode, + dataType = "results", + dataProfile = "basicphyschem" +) +ncol(qw_data) ``` ### Python - ```{python} -#| eval: false -ts_avail, ts_me = waterdata.get_time_series_metadata( - monitoring_location_id="USGS-04183500" +#| eval: !expr evaluate_python +site = "USGS-01631000" +pcode = "00660" + +qw_data, md_qw = waterdata.get_samples( + monitoringLocationIdentifier = site, + usgsPCode = pcode, + service = "results", + profile = "basicphyschem", ) + +qw_data.shape[1] ``` ::: -```{r} -#| echo: false -dt_me( - ts_available |> - sf::st_drop_geometry() |> - select( - parameter_name, - parameter_code, - statistic_id, - begin, - end, - computation_identifier - ), - page_length = 6 -) -``` +That's a LOT of columns returned. ::: footer ::: -## Data Discovery: Discrete {.smaller} +## USGS Samples Data Notes: Data Types and Profiles ::: {.panel-tabset} ### R -```{r} -discrete_available <- summarize_waterdata_samples( - monitoringLocationIdentifier = "USGS-04183500" -) -``` - - -::: +* There are 2 arguments that dictate what kind of data is returned + - "dataType" defines what kind of data comes back + - "dataProfile" defines what columns from that type come back -```{r} -#| echo: false -dt_me( - discrete_available |> - select( - characteristicUserSupplied, - resultCount, - activityCount, - firstActivity, - mostRecentActivity - ), - page_length = 6 -) -``` +### Python -::: footer +* There are 2 parameters that dictate what kind of data is returned + - "service" defines what kind of data comes back + - "profile" defines what columns from that type come back ::: -## characteristicUserSupplied {.smaller} - -* characteristicUserSupplied can be an input to `read_waterdata_sample` +## {.smaller} ::: {.panel-tabset} ### R ```{r} -discrete1 <- read_waterdata_samples( - characteristicUserSupplied = "Phosphorus as phosphorus, water, unfiltered", - monitoringLocationIdentifier = "USGS-04183500" +#| echo: false +df <- tibble( + dataType = c( + "results", + "locations", + "activities", + "projects", + "organizations" + ), + Description = c( + "Results data and metadata for measures and observations matching your query", + "Find monitoring locations that have data matching your query", + "Information about the monitoring activities conducted that produced data", + "Information on the projects that have results matching your data query", + "Information about the organizations that have provided data that matches your query" + ), + dataProfile = c( + 'fullphyschem
basicphyschem
fullbio
basicbio
narrow
resultdetectionquantitationlimit
labsampleprep
count', + 'site
count', + 'sampact
actmetric
actgroup
ncount', + 'project
projectmonitoringlocationweight', + 'organization
count' + ) ) -nrow(discrete1) + +dt_me(df, escape = FALSE, paging = FALSE) ``` ### Python -```{python} -#| eval: !expr evaluate_python -discrete1, discrete1_me = waterdata.get_samples( - characteristicUserSupplied = "Phosphorus as phosphorus, water, unfiltered", - monitoringLocationIdentifier = "USGS-04183500" -) -discrete1.shape[1] +```{r} +#| echo: false +names(df) <- c("service", "Description", "profile") + +dt_me(df, escape = FALSE, paging = FALSE) ``` ::: +::: footer + +::: + +## Data Sources + +The examples in these slides got all the data from the Water Data API: + + + +The `dataretrieval` packages also include functions to access: + +* [Water Quality Portal](