{"id":14067380,"url":"https://github.com/eco4cast/neon4cast","last_synced_at":"2025-07-30T00:33:22.621Z","repository":{"id":43093664,"uuid":"348136724","full_name":"eco4cast/neon4cast","owner":"eco4cast","description":"A helper R package for the neon4cast challenge","archived":false,"fork":false,"pushed_at":"2025-07-11T15:12:23.000Z","size":1366,"stargazers_count":9,"open_issues_count":5,"forks_count":8,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-07-11T16:44:28.029Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eco4cast.png","metadata":{"files":{"readme":"README.Rmd","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-15T22:09:46.000Z","updated_at":"2025-07-11T15:12:27.000Z","dependencies_parsed_at":"2024-01-17T19:29:01.331Z","dependency_job_id":"7de93a67-52aa-4261-b8ce-683fabda3d2d","html_url":"https://github.com/eco4cast/neon4cast","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/eco4cast/neon4cast","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eco4cast%2Fneon4cast","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eco4cast%2Fneon4cast/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eco4cast%2Fneon4cast/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eco4cast%2Fneon4cast/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eco4cast","download_url":"https://codeload.github.com/eco4cast/neon4cast/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eco4cast%2Fneon4cast/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267785809,"owners_count":24144122,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-29T02:00:12.549Z","response_time":2574,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-13T07:05:33.945Z","updated_at":"2025-07-30T00:33:21.389Z","avatar_url":"https://github.com/eco4cast.png","language":"R","funding_links":[],"categories":["R"],"sub_categories":[],"readme":"---\noutput: \n  github_document:\n    df_print: tibble\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r, include = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  warning = FALSE,\n  message = FALSE,\n  comment = \"#\u003e\",\n  fig.path = \"man/figures/README-\",\n  out.width = \"100%\"\n)\n```\n\n# neon4cast\n\n\u003c!-- badges: start --\u003e\n  [![R-CMD-check](https://github.com/eco4cast/neon4cast/workflows/R-CMD-check/badge.svg)](https://github.com/eco4cast/neon4cast/actions)\n\u003c!-- badges: end --\u003e\n\n`neon4cast` provides a collection of convenient helper utilities for anyone entering the EFI NEON Forecasting Challenge.  \n\n## Installation\n\nYou can install the development version from [GitHub](https://github.com/) with:\n\n``` r\n# install.packages(\"remotes\")\nremotes::install_github(\"eco4cast/neon4cast\")\n```\n\n## Examples\n\n```{r message=FALSE}\nlibrary(neon4cast)\nlibrary(tidyverse)\nlibrary(fable)\nlibrary(tsibble)\n```\n\nDownload and read in the current target file for the Aquatics theme.  For convenience, we read this in as \na timeseries object, noting that the time is in the 'time' column, and timeseries are replicated over sites.\n\n```{r, message=FALSE}\ntarget \u003c- read_csv(\"https://data.ecoforecast.org/neon4cast-targets/aquatics/aquatics-targets.csv.gz\")\n\naquatic \u003c- target %\u003e% \n    pivot_wider(names_from = \"variable\", values_from = \"observation\") %\u003e%\n    as_tsibble(index = datetime, key = site_id)\n```\n\nCreate a 35 day forecast for each variable, `oxygen`, and `temperature`.  For illustrative purposes, we'll use the `fable` package because it is concise and well documented.  We make separate forecasts for each of the two variables before reformatting them and combining them.  Note the use of `efi_format` helper function from the `neon4cast` package, which merely replaces the special `\u003cS3:distribution\u003e` column used by fable with something we can write to text: either columns with a mean/sd (for normal distributions) or otherwise random draws from the distributions. \n\nSo that we can score our forecast right away instead of waiting for next month's data, we will filter out the most recent data available first.  \n\n```{r, message = FALSE}\n# drop last 35 days and use explicit NAs for gaps in timeseries\nblinded_aquatic \u003c- aquatic %\u003e%\n  filter(datetime \u003c max(datetime) - 35) %\u003e% \n  fill_gaps()\n\n# A simple random walk forecast, see ?fable::RW\noxygen_fc \u003c- blinded_aquatic %\u003e%\n  model(benchmark_rw = RW(oxygen)) %\u003e%\n  forecast(h = \"35 days\") %\u003e%\n  efi_format()\n\n## also use random walk for temperature\ntemperature_fc \u003c- blinded_aquatic  %\u003e% \n  model(benchmark_rw = RW(temperature)) %\u003e%\n  forecast(h = \"35 days\") %\u003e%\n  efi_format_ensemble()\n\n# stack into single table\nforecast \u003c- bind_rows(oxygen_fc, temperature_fc) \n\n## Write the forecast to a file following EFI naming conventions:\nforecast_file \u003c- glue::glue(\"{theme}-{date}-{team}.csv.gz\",\n                            theme = \"aquatics\", \n                            date=Sys.Date(),\n                            team = \"benchmark_rw\")\nwrite_csv(forecast, forecast_file)\n\n```\n\n### Score forecast locally\n\nScores for valid forecasts should appear at \u003chttps://shiny.ecoforecast.org\u003e the day after they are submitted.  However, it is often more convenient to generate scores locally. Note that the \"score\" simply the `crps_sample` (for ensemble forecasts) or `crps_norm` (for summary statistic forecasts) score from the\n`scoringRules` R package, for each unique prediction (i.e. day/site/variable tuple). \n\nNote that scores are only possible once the data becomes available in the corresponding targets file! \n\n```{r message=FALSE}\n\nscores \u003c- score(forecast, target)\n\n# The resulting data.frame scores each day for each site, but is also easy to summarize:\nscores %\u003e% \n  group_by(variable) %\u003e% \n  summarise(mean_crps = mean(crps, na.rm=TRUE),\n            mean_logs =  mean(logs, na.rm=TRUE))\n```\n\n\n### Validate a forecast file\n\nValidating a forecast file runs the same automated checks as the EFI server, verifying that the \ndata is in the correct format for the appropriate challenge.  Helpful errors or warnings will \ndisplayed on any invalid formats.  Note that the validator accepts files in `.csv` (optionally \ncompressed as `.csv.gz`) or netcdf.\n\n```{r}\nforecast_output_validator(forecast_file)\n```\n\n\n\n### Access EFI snapshots of NOAA forecasts at NEON sites\n\nMany forecasts will want to make use of weather forecasts as potential drivers. EFI downscales NOAA GEFS 35-day forecast products at each NEON site and makes this data available. These helper functions provide convenient access for downloading and stacking the individual forecast files. \n\n```{r}\naq_sites \u003c- unique(aquatic$site_id)\n\nnoaa \u003c- noaa_stage1() \n\nref_date \u003c- Sys.Date()-1\n# Average temperature forecast across ensembles by site_id, next 240 hours\nnoaa_temp \u003c- noaa |\u003e\n  filter(site_id %in% aq_sites,\n         reference_datetime == ref_date,\n         variable == \"TMP\", \n         horizon \u003c 240) |\u003e\n  group_by(site_id, reference_datetime, datetime, variable) |\u003e\n  summarise(prediction = mean(prediction, na.rm=TRUE)) |\u003e\n  collect()\n\nnoaa_temp\n```\n\nAdditional NOAA products are available. `stage2` product includes downscaling to hourly increments with fluxes corrected for solar geometries.\nThe stage3 product is assembled from all the 0-hour horizons, which may be more useful for calibrating models than reliance on direct measurements of meterological data, as the latter may differ systematically from the long-term forecast at any given site.\n\n\n### Submit a forecast\n\nWhen you are ready to submit your forecast to EFI:\n\n```{r eval=FALSE}\nsubmit(forecast_file)\n```\n\nIdeally you should include the optional `metadata = ` argument with your metadata file. \n\n### Other functions\n\nEncountered a bug?  Facing another challenge in participating in the challenge? Developed a cool approach you would like to share with the community? Open an [issue](https://github.com/eco4cast/neon4cast/issues) or [pull request](https://github.com/eco4cast/neon4cast/pulls) here!\n\n\n```{r include=FALSE}\nunlink(forecast_file)\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feco4cast%2Fneon4cast","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feco4cast%2Fneon4cast","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feco4cast%2Fneon4cast/lists"}