noaa-gml-file-reader 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- noaa_gml_file_reader-2.0.0/.gitattributes +2 -0
- noaa_gml_file_reader-2.0.0/.github/FUNDING.yml +16 -0
- noaa_gml_file_reader-2.0.0/.github/workflows/ci.yml +57 -0
- noaa_gml_file_reader-2.0.0/.github/workflows/publish.yml +79 -0
- noaa_gml_file_reader-2.0.0/.gitignore +25 -0
- noaa_gml_file_reader-2.0.0/CHANGELOG.md +62 -0
- noaa_gml_file_reader-2.0.0/CITATION.cff +22 -0
- noaa_gml_file_reader-2.0.0/LICENSE +21 -0
- noaa_gml_file_reader-2.0.0/PKG-INFO +137 -0
- noaa_gml_file_reader-2.0.0/README.md +113 -0
- noaa_gml_file_reader-2.0.0/docs/design/2026-07-07-gml-reader-v2.md +159 -0
- noaa_gml_file_reader-2.0.0/docs/format-notes.md +129 -0
- noaa_gml_file_reader-2.0.0/docs/plans/2026-07-07-repo-improvement-plan.md +172 -0
- noaa_gml_file_reader-2.0.0/docs/release-checklist.md +73 -0
- noaa_gml_file_reader-2.0.0/noaa_gml_file_reader/__init__.py +16 -0
- noaa_gml_file_reader-2.0.0/noaa_gml_file_reader/_reader.py +138 -0
- noaa_gml_file_reader-2.0.0/pyproject.toml +46 -0
- noaa_gml_file_reader-2.0.0/tests/fixtures/README.md +32 -0
- noaa_gml_file_reader-2.0.0/tests/fixtures/ch4_mlo_surface-flask_1_ccgg_event.txt +169 -0
- noaa_gml_file_reader-2.0.0/tests/fixtures/ch4_mlo_surface-flask_1_ccgg_month.txt +74 -0
- noaa_gml_file_reader-2.0.0/tests/fixtures/co2_mlo_surface-flask_1_ccgg_event.txt +169 -0
- noaa_gml_file_reader-2.0.0/tests/fixtures/co2_mlo_surface-flask_1_ccgg_month.txt +74 -0
- noaa_gml_file_reader-2.0.0/tests/fixtures/co2_mlo_surface-insitu_1_ccgg_HourlyData.txt +185 -0
- noaa_gml_file_reader-2.0.0/tests/test_reader.py +146 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# These are supported funding model platforms
|
|
2
|
+
|
|
3
|
+
github: [ErickShepherd] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
|
4
|
+
patreon: ErickShepherd # Replace with a single Patreon username
|
|
5
|
+
open_collective: # Replace with a single Open Collective username
|
|
6
|
+
ko_fi: # Replace with a single Ko-fi username
|
|
7
|
+
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
|
8
|
+
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
9
|
+
liberapay: # Replace with a single Liberapay username
|
|
10
|
+
issuehunt: # Replace with a single IssueHunt username
|
|
11
|
+
otechie: # Replace with a single Otechie username
|
|
12
|
+
<<<<<<< HEAD
|
|
13
|
+
custom: ['paypal.me/ErickEShepherd', 'https://cash.app/$ErickShepherd', 'https://venmo.com/ErickShepherd', 'https://erickshepherd.com/pay/bitcoin/'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
|
14
|
+
=======
|
|
15
|
+
custom: ['paypal.me/ErickEShepherd', 'https://cash.app/$ErickShepherd', 'https://erickshepherd.com/pay/bitcoin/'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
|
16
|
+
>>>>>>> 8106f864c38116f9cb7259da31f4d22a813686a1
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
lint:
|
|
13
|
+
name: ruff
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
- name: Install ruff
|
|
21
|
+
run: python -m pip install --upgrade pip ruff
|
|
22
|
+
- name: Ruff check
|
|
23
|
+
run: ruff check .
|
|
24
|
+
|
|
25
|
+
test:
|
|
26
|
+
name: test (py${{ matrix.python-version }})
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
strategy:
|
|
29
|
+
fail-fast: false
|
|
30
|
+
matrix:
|
|
31
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
- uses: actions/setup-python@v5
|
|
35
|
+
with:
|
|
36
|
+
python-version: ${{ matrix.python-version }}
|
|
37
|
+
- name: Install package + test deps
|
|
38
|
+
run: |
|
|
39
|
+
python -m pip install --upgrade pip
|
|
40
|
+
python -m pip install -e '.[test]'
|
|
41
|
+
- name: Pytest
|
|
42
|
+
run: python -m pytest -q
|
|
43
|
+
|
|
44
|
+
build:
|
|
45
|
+
name: build + twine check
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
- uses: actions/setup-python@v5
|
|
50
|
+
with:
|
|
51
|
+
python-version: "3.12"
|
|
52
|
+
- name: Install build tooling
|
|
53
|
+
run: python -m pip install --upgrade pip build twine
|
|
54
|
+
- name: Build sdist + wheel
|
|
55
|
+
run: python -m build
|
|
56
|
+
- name: Twine check
|
|
57
|
+
run: twine check dist/*
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Publishes the package via PyPI Trusted Publishing (OIDC) — no API token is
|
|
4
|
+
# stored anywhere (not in the repo, not in GitHub secrets). PyPI verifies the
|
|
5
|
+
# workflow's short-lived OIDC identity instead.
|
|
6
|
+
#
|
|
7
|
+
# Two ways to run:
|
|
8
|
+
# * A published GitHub Release -> builds and publishes to PyPI.
|
|
9
|
+
# * Manual "Run workflow" -> choose testpypi (dry run) or pypi.
|
|
10
|
+
#
|
|
11
|
+
# The publish jobs run in protected GitHub Environments (`pypi` / `testpypi`).
|
|
12
|
+
# Add yourself as a required reviewer on each (repo Settings -> Environments)
|
|
13
|
+
# so every upload pauses for your one-click approval before it leaves.
|
|
14
|
+
|
|
15
|
+
on:
|
|
16
|
+
release:
|
|
17
|
+
types: [released] # full releases only — pre-releases never auto-publish
|
|
18
|
+
workflow_dispatch:
|
|
19
|
+
inputs:
|
|
20
|
+
target:
|
|
21
|
+
description: "Where to publish"
|
|
22
|
+
type: choice
|
|
23
|
+
options: [testpypi, pypi]
|
|
24
|
+
default: testpypi
|
|
25
|
+
|
|
26
|
+
permissions:
|
|
27
|
+
contents: read
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
build:
|
|
31
|
+
name: Build sdist + wheel
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
- uses: actions/setup-python@v5
|
|
36
|
+
with:
|
|
37
|
+
python-version: "3.12"
|
|
38
|
+
- name: Build
|
|
39
|
+
run: |
|
|
40
|
+
python -m pip install --upgrade build twine
|
|
41
|
+
python -m build
|
|
42
|
+
python -m twine check dist/*
|
|
43
|
+
- uses: actions/upload-artifact@v4
|
|
44
|
+
with:
|
|
45
|
+
name: dist
|
|
46
|
+
path: dist/
|
|
47
|
+
|
|
48
|
+
publish-testpypi:
|
|
49
|
+
name: Publish to TestPyPI
|
|
50
|
+
needs: build
|
|
51
|
+
if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
environment: testpypi
|
|
54
|
+
permissions:
|
|
55
|
+
id-token: write
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/download-artifact@v4
|
|
58
|
+
with:
|
|
59
|
+
name: dist
|
|
60
|
+
path: dist/
|
|
61
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
62
|
+
with:
|
|
63
|
+
repository-url: https://test.pypi.org/legacy/
|
|
64
|
+
skip-existing: true # re-dispatching a dry run for an existing version won't fail
|
|
65
|
+
|
|
66
|
+
publish-pypi:
|
|
67
|
+
name: Publish to PyPI
|
|
68
|
+
needs: build
|
|
69
|
+
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.target == 'pypi')
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
environment: pypi
|
|
72
|
+
permissions:
|
|
73
|
+
id-token: write
|
|
74
|
+
steps:
|
|
75
|
+
- uses: actions/download-artifact@v4
|
|
76
|
+
with:
|
|
77
|
+
name: dist
|
|
78
|
+
path: dist/
|
|
79
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Byte-compiled / optimized files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Build / packaging artifacts
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
*.egg
|
|
11
|
+
.eggs/
|
|
12
|
+
wheels/
|
|
13
|
+
|
|
14
|
+
# Virtual environments
|
|
15
|
+
.venv/
|
|
16
|
+
venv/
|
|
17
|
+
env/
|
|
18
|
+
ENV/
|
|
19
|
+
|
|
20
|
+
# Test / lint / type caches
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
.ruff_cache/
|
|
23
|
+
.mypy_cache/
|
|
24
|
+
.coverage
|
|
25
|
+
htmlcov/
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [2.0.0] — 2026-07-08
|
|
8
|
+
|
|
9
|
+
Modernization release. **Breaking** — the package is renamed and unparseable
|
|
10
|
+
input now raises instead of returning an empty DataFrame.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- **Renamed** the package: import `noaa_esrl_gmd_file_reader` →
|
|
14
|
+
`noaa_gml_file_reader`, distribution `noaa-gml-file-reader`. NOAA renamed
|
|
15
|
+
ESRL's Global Monitoring Division (GMD) to the Global Monitoring Laboratory
|
|
16
|
+
(GML) in 2020.
|
|
17
|
+
- **`read_data` now auto-detects the header dialect** and parses both the legacy
|
|
18
|
+
`# number_of_header_lines:` format and the current `# header_lines :` format,
|
|
19
|
+
extracting column names correctly for each.
|
|
20
|
+
- Documented missing-value sentinels (`-999.99`, `-999.999`, `nan`) are mapped to
|
|
21
|
+
`NaN` so numeric columns parse numeric.
|
|
22
|
+
- **Relicensed** AGPL-3.0 → MIT.
|
|
23
|
+
- Modern packaging: `pyproject.toml` (hatchling), package split into
|
|
24
|
+
`_reader.py`, full type hints, and updated docstrings.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- `UnrecognizedFormatError` (carries the file path and its first line), raised on
|
|
28
|
+
unrecognized, malformed, truncated, or empty input.
|
|
29
|
+
- Offline pytest suite over committed fixtures and a GitHub Actions CI workflow
|
|
30
|
+
(ruff + pytest on Python 3.10–3.13 + `python -m build` + `twine check`).
|
|
31
|
+
|
|
32
|
+
### Removed
|
|
33
|
+
- The silent empty-DataFrame return on parse failure (v1 behavior) — it made a
|
|
34
|
+
parse failure indistinguishable from an empty file, so v1 failed silently on
|
|
35
|
+
every current NOAA file.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
- Current (post-2020) NOAA GML files now parse instead of returning empty.
|
|
39
|
+
- The inconsistent GPL/AGPL wording in the v1 module docstring (removed; the
|
|
40
|
+
LICENSE file governs).
|
|
41
|
+
|
|
42
|
+
## [1.0.2] — 2020-04-13
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
- Linted the module: removed unused imports, added an encoding declaration, and
|
|
46
|
+
removed trailing whitespace.
|
|
47
|
+
|
|
48
|
+
## [1.0.1] — 2020-03-29
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
- `read_data` now returns an empty `pandas.DataFrame` (instead of `None`) when it
|
|
52
|
+
fails to read a file.
|
|
53
|
+
|
|
54
|
+
## [1.0.0] — 2020-03-25
|
|
55
|
+
|
|
56
|
+
### Added
|
|
57
|
+
- Initial build developed and released.
|
|
58
|
+
|
|
59
|
+
[2.0.0]: https://github.com/ErickShepherd/noaa-gml-file-reader/releases/tag/v2.0.0
|
|
60
|
+
[1.0.2]: https://github.com/ErickShepherd/noaa-gml-file-reader/releases/tag/v1.0.2
|
|
61
|
+
[1.0.1]: https://github.com/ErickShepherd/noaa-gml-file-reader/releases/tag/v1.0.1
|
|
62
|
+
[1.0.0]: https://github.com/ErickShepherd/noaa-gml-file-reader/releases/tag/v1.0.0
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it using these metadata."
|
|
3
|
+
title: "noaa-gml-file-reader"
|
|
4
|
+
abstract: "Read NOAA GML atmospheric trace-gas ASCII data files as pandas DataFrames."
|
|
5
|
+
type: software
|
|
6
|
+
authors:
|
|
7
|
+
- family-names: "Shepherd"
|
|
8
|
+
given-names: "Erick Edward"
|
|
9
|
+
email: "Contact@ErickShepherd.com"
|
|
10
|
+
version: "2.0.0"
|
|
11
|
+
date-released: "2026-07-08"
|
|
12
|
+
license: MIT
|
|
13
|
+
repository-code: "https://github.com/ErickShepherd/noaa-gml-file-reader"
|
|
14
|
+
url: "https://github.com/ErickShepherd/noaa-gml-file-reader"
|
|
15
|
+
keywords:
|
|
16
|
+
- NOAA
|
|
17
|
+
- GML
|
|
18
|
+
- atmospheric
|
|
19
|
+
- trace gases
|
|
20
|
+
- CO2
|
|
21
|
+
- CH4
|
|
22
|
+
- pandas
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020-2026 Erick Edward Shepherd
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: noaa-gml-file-reader
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Read NOAA GML atmospheric trace-gas ASCII data files as pandas DataFrames.
|
|
5
|
+
Project-URL: Homepage, https://github.com/ErickShepherd/noaa-gml-file-reader
|
|
6
|
+
Author-email: Erick Edward Shepherd <Contact@ErickShepherd.com>
|
|
7
|
+
Maintainer-email: Erick Edward Shepherd <Contact@ErickShepherd.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: CH4,CO2,GMD,GML,NOAA,atmospheric,pandas,trace gases
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: pandas>=2.0
|
|
21
|
+
Provides-Extra: test
|
|
22
|
+
Requires-Dist: pytest; extra == 'test'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# noaa-gml-file-reader
|
|
26
|
+
|
|
27
|
+
[](https://github.com/ErickShepherd/noaa-gml-file-reader/actions/workflows/ci.yml)
|
|
28
|
+

|
|
29
|
+
[](LICENSE)
|
|
30
|
+
|
|
31
|
+
Read [NOAA GML](https://gml.noaa.gov) atmospheric trace-gas ASCII data files as
|
|
32
|
+
[`pandas.DataFrame`](https://pandas.pydata.org/) objects — one function,
|
|
33
|
+
`read_data(path)`.
|
|
34
|
+
|
|
35
|
+
## What this reads
|
|
36
|
+
|
|
37
|
+
NOAA's Global Monitoring Laboratory (GML) publishes long-term atmospheric
|
|
38
|
+
trace-gas measurements (CO₂, CH₄, and others) as whitespace-delimited ASCII
|
|
39
|
+
files with a `#`-commented header — surface-flask **event** and **monthly**
|
|
40
|
+
series, in-situ **hourly**/daily series, and more, at
|
|
41
|
+
[gml.noaa.gov/aftp/data/trace_gases](https://gml.noaa.gov/aftp/data/trace_gases/).
|
|
42
|
+
This package turns one of those files into a tidy DataFrame, with the header's
|
|
43
|
+
column names and the documented missing-value sentinels handled for you.
|
|
44
|
+
|
|
45
|
+
> **Lineage (GMD → GML).** NOAA renamed ESRL's Global Monitoring *Division*
|
|
46
|
+
> (GMD) to the Global Monitoring *Laboratory* (GML) in 2020, and the file header
|
|
47
|
+
> format changed with it. This package was originally `noaa_esrl_gmd_file_reader`
|
|
48
|
+
> (2020); v2 renames it to **noaa-gml-file-reader** (import `noaa_gml_file_reader`)
|
|
49
|
+
> and reads the current header format. See the breaking-change note below.
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install noaa-gml-file-reader
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Requires Python ≥ 3.10 and pandas ≥ 2.0. (Not yet on PyPI — install from source:
|
|
58
|
+
`pip install git+https://github.com/ErickShepherd/noaa-gml-file-reader`.)
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from noaa_gml_file_reader import read_data
|
|
64
|
+
|
|
65
|
+
df = read_data("co2_mlo_surface-flask_1_ccgg_month.txt")
|
|
66
|
+
print(df.head())
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Real output (from the committed test fixture of the above Mauna Loa monthly file):
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
site year month value
|
|
73
|
+
0 MLO 1969 8 322.50
|
|
74
|
+
1 MLO 1969 9 321.36
|
|
75
|
+
2 MLO 1969 10 320.74
|
|
76
|
+
3 MLO 1969 11 321.98
|
|
77
|
+
4 MLO 1969 12 323.78
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Columns come straight from the file's header; numeric columns are numeric
|
|
81
|
+
(`value` is `float64`, `year`/`month` are `int64`), and NOAA's missing-value
|
|
82
|
+
sentinels (`-999.99`, `-999.999`, `nan`) are parsed as `NaN`.
|
|
83
|
+
|
|
84
|
+
## Supported dialects
|
|
85
|
+
|
|
86
|
+
`read_data` auto-detects the header dialect — you don't need to know NOAA's
|
|
87
|
+
format history to read a file:
|
|
88
|
+
|
|
89
|
+
| dialect | header key | column names from | seen in |
|
|
90
|
+
| --- | --- | --- | --- |
|
|
91
|
+
| **current** | `# header_lines : N` | the bare column row that is the last header line | flask **event**, **in-situ** (hourly/daily) |
|
|
92
|
+
| **legacy** (2020) | `# number_of_header_lines: N` | the `# data_fields:` header line | flask **monthly** |
|
|
93
|
+
|
|
94
|
+
Supported products are those catalogued in [`docs/format-notes.md`](docs/format-notes.md)
|
|
95
|
+
(CO₂/CH₄ surface-flask event + monthly, CO₂ in-situ hourly, at MLO). The header
|
|
96
|
+
grammar is site- and gas-agnostic, so other stations/species in the same product
|
|
97
|
+
families parse the same way.
|
|
98
|
+
|
|
99
|
+
## Errors
|
|
100
|
+
|
|
101
|
+
An unparseable file **raises** `UnrecognizedFormatError` (carrying the path and
|
|
102
|
+
the file's first line) rather than returning data:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from noaa_gml_file_reader import read_data, UnrecognizedFormatError
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
df = read_data("not-a-noaa-file.txt")
|
|
109
|
+
except UnrecognizedFormatError as err:
|
|
110
|
+
print(err)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## v2 breaking changes
|
|
114
|
+
|
|
115
|
+
v2 is a breaking release (hence the major bump):
|
|
116
|
+
|
|
117
|
+
- **Renamed** `noaa_esrl_gmd_file_reader` → `noaa_gml_file_reader`
|
|
118
|
+
(distribution `noaa-gml-file-reader`).
|
|
119
|
+
- **Raises `UnrecognizedFormatError`** on unrecognized/malformed input. v1
|
|
120
|
+
returned an *empty DataFrame* on any parse failure — silently indistinguishable
|
|
121
|
+
from "the file had no data" — which meant v1 failed silently on every current
|
|
122
|
+
(post-2020) NOAA file. If you relied on the empty-DataFrame behavior, catch the
|
|
123
|
+
exception instead.
|
|
124
|
+
- **Relicensed** AGPL-3.0 → MIT.
|
|
125
|
+
|
|
126
|
+
## Data & citation
|
|
127
|
+
|
|
128
|
+
The data are NOAA GML's, not this package's. Please cite the measurements per
|
|
129
|
+
NOAA GML's guidance — each product directory under
|
|
130
|
+
[gml.noaa.gov/aftp/data/trace_gases](https://gml.noaa.gov/aftp/data/trace_gases/)
|
|
131
|
+
ships a species-specific README with the citation text and data providers (the
|
|
132
|
+
file headers also carry contact and reciprocity information). This library only
|
|
133
|
+
parses the files; it does not download them.
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# noaa-gml-file-reader
|
|
2
|
+
|
|
3
|
+
[](https://github.com/ErickShepherd/noaa-gml-file-reader/actions/workflows/ci.yml)
|
|
4
|
+

|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Read [NOAA GML](https://gml.noaa.gov) atmospheric trace-gas ASCII data files as
|
|
8
|
+
[`pandas.DataFrame`](https://pandas.pydata.org/) objects — one function,
|
|
9
|
+
`read_data(path)`.
|
|
10
|
+
|
|
11
|
+
## What this reads
|
|
12
|
+
|
|
13
|
+
NOAA's Global Monitoring Laboratory (GML) publishes long-term atmospheric
|
|
14
|
+
trace-gas measurements (CO₂, CH₄, and others) as whitespace-delimited ASCII
|
|
15
|
+
files with a `#`-commented header — surface-flask **event** and **monthly**
|
|
16
|
+
series, in-situ **hourly**/daily series, and more, at
|
|
17
|
+
[gml.noaa.gov/aftp/data/trace_gases](https://gml.noaa.gov/aftp/data/trace_gases/).
|
|
18
|
+
This package turns one of those files into a tidy DataFrame, with the header's
|
|
19
|
+
column names and the documented missing-value sentinels handled for you.
|
|
20
|
+
|
|
21
|
+
> **Lineage (GMD → GML).** NOAA renamed ESRL's Global Monitoring *Division*
|
|
22
|
+
> (GMD) to the Global Monitoring *Laboratory* (GML) in 2020, and the file header
|
|
23
|
+
> format changed with it. This package was originally `noaa_esrl_gmd_file_reader`
|
|
24
|
+
> (2020); v2 renames it to **noaa-gml-file-reader** (import `noaa_gml_file_reader`)
|
|
25
|
+
> and reads the current header format. See the breaking-change note below.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install noaa-gml-file-reader
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Requires Python ≥ 3.10 and pandas ≥ 2.0. (Not yet on PyPI — install from source:
|
|
34
|
+
`pip install git+https://github.com/ErickShepherd/noaa-gml-file-reader`.)
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from noaa_gml_file_reader import read_data
|
|
40
|
+
|
|
41
|
+
df = read_data("co2_mlo_surface-flask_1_ccgg_month.txt")
|
|
42
|
+
print(df.head())
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Real output (from the committed test fixture of the above Mauna Loa monthly file):
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
site year month value
|
|
49
|
+
0 MLO 1969 8 322.50
|
|
50
|
+
1 MLO 1969 9 321.36
|
|
51
|
+
2 MLO 1969 10 320.74
|
|
52
|
+
3 MLO 1969 11 321.98
|
|
53
|
+
4 MLO 1969 12 323.78
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Columns come straight from the file's header; numeric columns are numeric
|
|
57
|
+
(`value` is `float64`, `year`/`month` are `int64`), and NOAA's missing-value
|
|
58
|
+
sentinels (`-999.99`, `-999.999`, `nan`) are parsed as `NaN`.
|
|
59
|
+
|
|
60
|
+
## Supported dialects
|
|
61
|
+
|
|
62
|
+
`read_data` auto-detects the header dialect — you don't need to know NOAA's
|
|
63
|
+
format history to read a file:
|
|
64
|
+
|
|
65
|
+
| dialect | header key | column names from | seen in |
|
|
66
|
+
| --- | --- | --- | --- |
|
|
67
|
+
| **current** | `# header_lines : N` | the bare column row that is the last header line | flask **event**, **in-situ** (hourly/daily) |
|
|
68
|
+
| **legacy** (2020) | `# number_of_header_lines: N` | the `# data_fields:` header line | flask **monthly** |
|
|
69
|
+
|
|
70
|
+
Supported products are those catalogued in [`docs/format-notes.md`](docs/format-notes.md)
|
|
71
|
+
(CO₂/CH₄ surface-flask event + monthly, CO₂ in-situ hourly, at MLO). The header
|
|
72
|
+
grammar is site- and gas-agnostic, so other stations/species in the same product
|
|
73
|
+
families parse the same way.
|
|
74
|
+
|
|
75
|
+
## Errors
|
|
76
|
+
|
|
77
|
+
An unparseable file **raises** `UnrecognizedFormatError` (carrying the path and
|
|
78
|
+
the file's first line) rather than returning data:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from noaa_gml_file_reader import read_data, UnrecognizedFormatError
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
df = read_data("not-a-noaa-file.txt")
|
|
85
|
+
except UnrecognizedFormatError as err:
|
|
86
|
+
print(err)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## v2 breaking changes
|
|
90
|
+
|
|
91
|
+
v2 is a breaking release (hence the major bump):
|
|
92
|
+
|
|
93
|
+
- **Renamed** `noaa_esrl_gmd_file_reader` → `noaa_gml_file_reader`
|
|
94
|
+
(distribution `noaa-gml-file-reader`).
|
|
95
|
+
- **Raises `UnrecognizedFormatError`** on unrecognized/malformed input. v1
|
|
96
|
+
returned an *empty DataFrame* on any parse failure — silently indistinguishable
|
|
97
|
+
from "the file had no data" — which meant v1 failed silently on every current
|
|
98
|
+
(post-2020) NOAA file. If you relied on the empty-DataFrame behavior, catch the
|
|
99
|
+
exception instead.
|
|
100
|
+
- **Relicensed** AGPL-3.0 → MIT.
|
|
101
|
+
|
|
102
|
+
## Data & citation
|
|
103
|
+
|
|
104
|
+
The data are NOAA GML's, not this package's. Please cite the measurements per
|
|
105
|
+
NOAA GML's guidance — each product directory under
|
|
106
|
+
[gml.noaa.gov/aftp/data/trace_gases](https://gml.noaa.gov/aftp/data/trace_gases/)
|
|
107
|
+
ships a species-specific README with the citation text and data providers (the
|
|
108
|
+
file headers also carry contact and reciprocity information). This library only
|
|
109
|
+
parses the files; it does not download them.
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
MIT — see [LICENSE](LICENSE).
|