SeisAug 0.2.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.
- seisaug-0.2.0/MANIFEST.in +2 -0
- seisaug-0.2.0/PKG-INFO +206 -0
- seisaug-0.2.0/README.md +171 -0
- seisaug-0.2.0/pyproject.toml +62 -0
- seisaug-0.2.0/setup.cfg +4 -0
- seisaug-0.2.0/src/SeisAug.egg-info/PKG-INFO +206 -0
- seisaug-0.2.0/src/SeisAug.egg-info/SOURCES.txt +20 -0
- seisaug-0.2.0/src/SeisAug.egg-info/dependency_links.txt +1 -0
- seisaug-0.2.0/src/SeisAug.egg-info/entry_points.txt +2 -0
- seisaug-0.2.0/src/SeisAug.egg-info/requires.txt +14 -0
- seisaug-0.2.0/src/SeisAug.egg-info/top_level.txt +1 -0
- seisaug-0.2.0/src/seisaug/__init__.py +25 -0
- seisaug-0.2.0/src/seisaug/__main__.py +4 -0
- seisaug-0.2.0/src/seisaug/cli.py +115 -0
- seisaug-0.2.0/src/seisaug/io.py +35 -0
- seisaug-0.2.0/src/seisaug/noise.py +60 -0
- seisaug-0.2.0/src/seisaug/plotting.py +63 -0
- seisaug-0.2.0/src/seisaug/signal.py +36 -0
- seisaug-0.2.0/src/seisaug/validate.py +41 -0
- seisaug-0.2.0/tests/test_noise.py +23 -0
- seisaug-0.2.0/tests/test_signal.py +29 -0
- seisaug-0.2.0/tests/test_validate.py +37 -0
seisaug-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: SeisAug
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Seismic waveform augmentation, STEAD-format I/O, plotting, and batch processing utilities
|
|
5
|
+
Author: Pragnath
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Pragnath/SeisAug
|
|
8
|
+
Project-URL: Repository, https://github.com/Pragnath/SeisAug
|
|
9
|
+
Project-URL: Issues, https://github.com/Pragnath/SeisAug/issues
|
|
10
|
+
Keywords: seismology,augmentation,obspy,earthquake,STEAD,machine-learning
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: numpy>=1.23
|
|
23
|
+
Requires-Dist: scipy>=1.10
|
|
24
|
+
Requires-Dist: matplotlib>=3.6
|
|
25
|
+
Requires-Dist: obspy>=1.4
|
|
26
|
+
Requires-Dist: pandas>=1.5
|
|
27
|
+
Requires-Dist: h5py>=3.8
|
|
28
|
+
Requires-Dist: ipywidgets>=8.0
|
|
29
|
+
Requires-Dist: tqdm>=4.65
|
|
30
|
+
Requires-Dist: pyyaml>=6.0
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
33
|
+
Requires-Dist: build>=1.2.1; extra == "dev"
|
|
34
|
+
Requires-Dist: twine>=5.1.1; extra == "dev"
|
|
35
|
+
|
|
36
|
+
# SeisAug
|
|
37
|
+
|
|
38
|
+
SeisAug is a Python package for **seismic waveform augmentation**, **STEAD-format data handling**, **validation**, and **basic visualization** for earthquake machine-learning workflows.
|
|
39
|
+
|
|
40
|
+
It is designed for researchers working with waveform datasets such as STEAD and for users building training pipelines for phase picking, event detection, and waveform classification.
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- Add seismic waveform augmentations such as noise injection, spikes, and time shifts
|
|
45
|
+
- Validate STEAD-format HDF5 and CSV datasets
|
|
46
|
+
- Read and process three-component waveform traces
|
|
47
|
+
- Plot SNR distributions from STEAD metadata
|
|
48
|
+
- Build batch augmentation pipelines from YAML configuration files
|
|
49
|
+
- Use a command-line interface for augmentation, validation, and plotting
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
Install from PyPI:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install SeisAug
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
For development:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
git clone https://github.com/Pragnath/SeisAug.git
|
|
63
|
+
cd SeisAug
|
|
64
|
+
pip install -e .[dev]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Command-Line Usage
|
|
68
|
+
|
|
69
|
+
After installation, the `seisaug` command is available.
|
|
70
|
+
|
|
71
|
+
### Show help
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
seisaug --help
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Batch augment a dataset
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
seisaug batch \
|
|
81
|
+
--input_hdf5 data/input.hdf5 \
|
|
82
|
+
--input_csv data/input.csv \
|
|
83
|
+
--output_hdf5 data/output_aug.hdf5 \
|
|
84
|
+
--output_csv data/output_aug.csv \
|
|
85
|
+
--config examples/augment_config.yaml
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Validate a STEAD-format dataset
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
seisaug validate \
|
|
92
|
+
--input_hdf5 data/output_aug.hdf5 \
|
|
93
|
+
--input_csv data/output_aug.csv
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Plot SNR distribution
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
seisaug snr \
|
|
100
|
+
--input_csv data/output_aug.csv \
|
|
101
|
+
--output_png snr_distribution.png
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Plot an augmentation grid
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
seisaug grid \
|
|
108
|
+
--input_hdf5 data/output_aug.hdf5 \
|
|
109
|
+
--trace_name trace_001 \
|
|
110
|
+
--augmented_names trace_001_aug1 trace_001_aug2 \
|
|
111
|
+
--output_png augmentation_grid.png
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Python Usage
|
|
115
|
+
|
|
116
|
+
### Validate a dataset
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from seisaug.validate import validate_stead_file
|
|
120
|
+
|
|
121
|
+
issues = validate_stead_file("data/input.hdf5", "data/input.csv")
|
|
122
|
+
|
|
123
|
+
if issues:
|
|
124
|
+
for issue in issues:
|
|
125
|
+
print(issue)
|
|
126
|
+
else:
|
|
127
|
+
print("Validation passed.")
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Apply a simple augmentation
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
import numpy as np
|
|
134
|
+
from seisaug.noise import add_spikes
|
|
135
|
+
|
|
136
|
+
x = np.ones(1000, dtype=np.float32)
|
|
137
|
+
y = add_spikes(x, num_spikes=5, spike_amp_ratio=0.5, seed=42)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Project Structure
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
SeisAug/
|
|
144
|
+
├── src/seisaug/
|
|
145
|
+
│ ├── __main__.py
|
|
146
|
+
│ ├── cli.py
|
|
147
|
+
│ ├── noise.py
|
|
148
|
+
│ ├── signal.py
|
|
149
|
+
│ ├── validate.py
|
|
150
|
+
│ └── ...
|
|
151
|
+
├── tests/
|
|
152
|
+
├── examples/
|
|
153
|
+
├── pyproject.toml
|
|
154
|
+
└── README.md
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Development
|
|
158
|
+
|
|
159
|
+
Run tests with:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
pytest -q
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Build the package with:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
python -m build
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Check package metadata and README rendering with:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
python -m twine check dist/*
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Use Cases
|
|
178
|
+
|
|
179
|
+
SeisAug is useful for:
|
|
180
|
+
|
|
181
|
+
- Earthquake phase-picking model training
|
|
182
|
+
- Data augmentation for seismic ML experiments
|
|
183
|
+
- STEAD-format dataset validation and preprocessing
|
|
184
|
+
- Quick CLI-based augmentation and visualization workflows
|
|
185
|
+
|
|
186
|
+
## Requirements
|
|
187
|
+
|
|
188
|
+
- Python 3.9+
|
|
189
|
+
- NumPy
|
|
190
|
+
- SciPy
|
|
191
|
+
- Matplotlib
|
|
192
|
+
- ObsPy
|
|
193
|
+
- pandas
|
|
194
|
+
- h5py
|
|
195
|
+
- PyYAML
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
MIT License
|
|
200
|
+
|
|
201
|
+
## Repository
|
|
202
|
+
|
|
203
|
+
Source code, issues, and updates:
|
|
204
|
+
|
|
205
|
+
- Repository: https://github.com/Pragnath/SeisAug
|
|
206
|
+
- Issues: https://github.com/Pragnath/SeisAug/issues
|
seisaug-0.2.0/README.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# SeisAug
|
|
2
|
+
|
|
3
|
+
SeisAug is a Python package for **seismic waveform augmentation**, **STEAD-format data handling**, **validation**, and **basic visualization** for earthquake machine-learning workflows.
|
|
4
|
+
|
|
5
|
+
It is designed for researchers working with waveform datasets such as STEAD and for users building training pipelines for phase picking, event detection, and waveform classification.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Add seismic waveform augmentations such as noise injection, spikes, and time shifts
|
|
10
|
+
- Validate STEAD-format HDF5 and CSV datasets
|
|
11
|
+
- Read and process three-component waveform traces
|
|
12
|
+
- Plot SNR distributions from STEAD metadata
|
|
13
|
+
- Build batch augmentation pipelines from YAML configuration files
|
|
14
|
+
- Use a command-line interface for augmentation, validation, and plotting
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Install from PyPI:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install SeisAug
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
For development:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/Pragnath/SeisAug.git
|
|
28
|
+
cd SeisAug
|
|
29
|
+
pip install -e .[dev]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Command-Line Usage
|
|
33
|
+
|
|
34
|
+
After installation, the `seisaug` command is available.
|
|
35
|
+
|
|
36
|
+
### Show help
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
seisaug --help
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Batch augment a dataset
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
seisaug batch \
|
|
46
|
+
--input_hdf5 data/input.hdf5 \
|
|
47
|
+
--input_csv data/input.csv \
|
|
48
|
+
--output_hdf5 data/output_aug.hdf5 \
|
|
49
|
+
--output_csv data/output_aug.csv \
|
|
50
|
+
--config examples/augment_config.yaml
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Validate a STEAD-format dataset
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
seisaug validate \
|
|
57
|
+
--input_hdf5 data/output_aug.hdf5 \
|
|
58
|
+
--input_csv data/output_aug.csv
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Plot SNR distribution
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
seisaug snr \
|
|
65
|
+
--input_csv data/output_aug.csv \
|
|
66
|
+
--output_png snr_distribution.png
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Plot an augmentation grid
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
seisaug grid \
|
|
73
|
+
--input_hdf5 data/output_aug.hdf5 \
|
|
74
|
+
--trace_name trace_001 \
|
|
75
|
+
--augmented_names trace_001_aug1 trace_001_aug2 \
|
|
76
|
+
--output_png augmentation_grid.png
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Python Usage
|
|
80
|
+
|
|
81
|
+
### Validate a dataset
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from seisaug.validate import validate_stead_file
|
|
85
|
+
|
|
86
|
+
issues = validate_stead_file("data/input.hdf5", "data/input.csv")
|
|
87
|
+
|
|
88
|
+
if issues:
|
|
89
|
+
for issue in issues:
|
|
90
|
+
print(issue)
|
|
91
|
+
else:
|
|
92
|
+
print("Validation passed.")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Apply a simple augmentation
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
import numpy as np
|
|
99
|
+
from seisaug.noise import add_spikes
|
|
100
|
+
|
|
101
|
+
x = np.ones(1000, dtype=np.float32)
|
|
102
|
+
y = add_spikes(x, num_spikes=5, spike_amp_ratio=0.5, seed=42)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Project Structure
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
SeisAug/
|
|
109
|
+
├── src/seisaug/
|
|
110
|
+
│ ├── __main__.py
|
|
111
|
+
│ ├── cli.py
|
|
112
|
+
│ ├── noise.py
|
|
113
|
+
│ ├── signal.py
|
|
114
|
+
│ ├── validate.py
|
|
115
|
+
│ └── ...
|
|
116
|
+
├── tests/
|
|
117
|
+
├── examples/
|
|
118
|
+
├── pyproject.toml
|
|
119
|
+
└── README.md
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
Run tests with:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
pytest -q
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Build the package with:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
python -m build
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Check package metadata and README rendering with:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
python -m twine check dist/*
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Use Cases
|
|
143
|
+
|
|
144
|
+
SeisAug is useful for:
|
|
145
|
+
|
|
146
|
+
- Earthquake phase-picking model training
|
|
147
|
+
- Data augmentation for seismic ML experiments
|
|
148
|
+
- STEAD-format dataset validation and preprocessing
|
|
149
|
+
- Quick CLI-based augmentation and visualization workflows
|
|
150
|
+
|
|
151
|
+
## Requirements
|
|
152
|
+
|
|
153
|
+
- Python 3.9+
|
|
154
|
+
- NumPy
|
|
155
|
+
- SciPy
|
|
156
|
+
- Matplotlib
|
|
157
|
+
- ObsPy
|
|
158
|
+
- pandas
|
|
159
|
+
- h5py
|
|
160
|
+
- PyYAML
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
|
|
164
|
+
MIT License
|
|
165
|
+
|
|
166
|
+
## Repository
|
|
167
|
+
|
|
168
|
+
Source code, issues, and updates:
|
|
169
|
+
|
|
170
|
+
- Repository: https://github.com/Pragnath/SeisAug
|
|
171
|
+
- Issues: https://github.com/Pragnath/SeisAug/issues
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "SeisAug"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Seismic waveform augmentation, STEAD-format I/O, plotting, and batch processing utilities"
|
|
9
|
+
dynamic = ["readme"]
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Pragnath"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["seismology", "augmentation", "obspy", "earthquake", "STEAD", "machine-learning"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Topic :: Scientific/Engineering :: Physics"
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"numpy>=1.23",
|
|
29
|
+
"scipy>=1.10",
|
|
30
|
+
"matplotlib>=3.6",
|
|
31
|
+
"obspy>=1.4",
|
|
32
|
+
"pandas>=1.5",
|
|
33
|
+
"h5py>=3.8",
|
|
34
|
+
"ipywidgets>=8.0",
|
|
35
|
+
"tqdm>=4.65",
|
|
36
|
+
"pyyaml>=6.0"
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
dev = [
|
|
41
|
+
"pytest>=7.0",
|
|
42
|
+
"build>=1.2.1",
|
|
43
|
+
"twine>=5.1.1"
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[project.urls]
|
|
47
|
+
Homepage = "https://github.com/Pragnath/SeisAug"
|
|
48
|
+
Repository = "https://github.com/Pragnath/SeisAug"
|
|
49
|
+
Issues = "https://github.com/Pragnath/SeisAug/issues"
|
|
50
|
+
|
|
51
|
+
[project.scripts]
|
|
52
|
+
seisaug = "seisaug.cli:main"
|
|
53
|
+
|
|
54
|
+
[tool.setuptools]
|
|
55
|
+
package-dir = {"" = "src"}
|
|
56
|
+
include-package-data = true
|
|
57
|
+
|
|
58
|
+
[tool.setuptools.packages.find]
|
|
59
|
+
where = ["src"]
|
|
60
|
+
|
|
61
|
+
[tool.setuptools.dynamic]
|
|
62
|
+
readme = {file = ["README.md"], content-type = "text/markdown"}
|
seisaug-0.2.0/setup.cfg
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: SeisAug
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Seismic waveform augmentation, STEAD-format I/O, plotting, and batch processing utilities
|
|
5
|
+
Author: Pragnath
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Pragnath/SeisAug
|
|
8
|
+
Project-URL: Repository, https://github.com/Pragnath/SeisAug
|
|
9
|
+
Project-URL: Issues, https://github.com/Pragnath/SeisAug/issues
|
|
10
|
+
Keywords: seismology,augmentation,obspy,earthquake,STEAD,machine-learning
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: numpy>=1.23
|
|
23
|
+
Requires-Dist: scipy>=1.10
|
|
24
|
+
Requires-Dist: matplotlib>=3.6
|
|
25
|
+
Requires-Dist: obspy>=1.4
|
|
26
|
+
Requires-Dist: pandas>=1.5
|
|
27
|
+
Requires-Dist: h5py>=3.8
|
|
28
|
+
Requires-Dist: ipywidgets>=8.0
|
|
29
|
+
Requires-Dist: tqdm>=4.65
|
|
30
|
+
Requires-Dist: pyyaml>=6.0
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
33
|
+
Requires-Dist: build>=1.2.1; extra == "dev"
|
|
34
|
+
Requires-Dist: twine>=5.1.1; extra == "dev"
|
|
35
|
+
|
|
36
|
+
# SeisAug
|
|
37
|
+
|
|
38
|
+
SeisAug is a Python package for **seismic waveform augmentation**, **STEAD-format data handling**, **validation**, and **basic visualization** for earthquake machine-learning workflows.
|
|
39
|
+
|
|
40
|
+
It is designed for researchers working with waveform datasets such as STEAD and for users building training pipelines for phase picking, event detection, and waveform classification.
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- Add seismic waveform augmentations such as noise injection, spikes, and time shifts
|
|
45
|
+
- Validate STEAD-format HDF5 and CSV datasets
|
|
46
|
+
- Read and process three-component waveform traces
|
|
47
|
+
- Plot SNR distributions from STEAD metadata
|
|
48
|
+
- Build batch augmentation pipelines from YAML configuration files
|
|
49
|
+
- Use a command-line interface for augmentation, validation, and plotting
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
Install from PyPI:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install SeisAug
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
For development:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
git clone https://github.com/Pragnath/SeisAug.git
|
|
63
|
+
cd SeisAug
|
|
64
|
+
pip install -e .[dev]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Command-Line Usage
|
|
68
|
+
|
|
69
|
+
After installation, the `seisaug` command is available.
|
|
70
|
+
|
|
71
|
+
### Show help
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
seisaug --help
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Batch augment a dataset
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
seisaug batch \
|
|
81
|
+
--input_hdf5 data/input.hdf5 \
|
|
82
|
+
--input_csv data/input.csv \
|
|
83
|
+
--output_hdf5 data/output_aug.hdf5 \
|
|
84
|
+
--output_csv data/output_aug.csv \
|
|
85
|
+
--config examples/augment_config.yaml
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Validate a STEAD-format dataset
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
seisaug validate \
|
|
92
|
+
--input_hdf5 data/output_aug.hdf5 \
|
|
93
|
+
--input_csv data/output_aug.csv
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Plot SNR distribution
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
seisaug snr \
|
|
100
|
+
--input_csv data/output_aug.csv \
|
|
101
|
+
--output_png snr_distribution.png
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Plot an augmentation grid
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
seisaug grid \
|
|
108
|
+
--input_hdf5 data/output_aug.hdf5 \
|
|
109
|
+
--trace_name trace_001 \
|
|
110
|
+
--augmented_names trace_001_aug1 trace_001_aug2 \
|
|
111
|
+
--output_png augmentation_grid.png
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Python Usage
|
|
115
|
+
|
|
116
|
+
### Validate a dataset
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from seisaug.validate import validate_stead_file
|
|
120
|
+
|
|
121
|
+
issues = validate_stead_file("data/input.hdf5", "data/input.csv")
|
|
122
|
+
|
|
123
|
+
if issues:
|
|
124
|
+
for issue in issues:
|
|
125
|
+
print(issue)
|
|
126
|
+
else:
|
|
127
|
+
print("Validation passed.")
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Apply a simple augmentation
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
import numpy as np
|
|
134
|
+
from seisaug.noise import add_spikes
|
|
135
|
+
|
|
136
|
+
x = np.ones(1000, dtype=np.float32)
|
|
137
|
+
y = add_spikes(x, num_spikes=5, spike_amp_ratio=0.5, seed=42)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Project Structure
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
SeisAug/
|
|
144
|
+
├── src/seisaug/
|
|
145
|
+
│ ├── __main__.py
|
|
146
|
+
│ ├── cli.py
|
|
147
|
+
│ ├── noise.py
|
|
148
|
+
│ ├── signal.py
|
|
149
|
+
│ ├── validate.py
|
|
150
|
+
│ └── ...
|
|
151
|
+
├── tests/
|
|
152
|
+
├── examples/
|
|
153
|
+
├── pyproject.toml
|
|
154
|
+
└── README.md
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Development
|
|
158
|
+
|
|
159
|
+
Run tests with:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
pytest -q
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Build the package with:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
python -m build
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Check package metadata and README rendering with:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
python -m twine check dist/*
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Use Cases
|
|
178
|
+
|
|
179
|
+
SeisAug is useful for:
|
|
180
|
+
|
|
181
|
+
- Earthquake phase-picking model training
|
|
182
|
+
- Data augmentation for seismic ML experiments
|
|
183
|
+
- STEAD-format dataset validation and preprocessing
|
|
184
|
+
- Quick CLI-based augmentation and visualization workflows
|
|
185
|
+
|
|
186
|
+
## Requirements
|
|
187
|
+
|
|
188
|
+
- Python 3.9+
|
|
189
|
+
- NumPy
|
|
190
|
+
- SciPy
|
|
191
|
+
- Matplotlib
|
|
192
|
+
- ObsPy
|
|
193
|
+
- pandas
|
|
194
|
+
- h5py
|
|
195
|
+
- PyYAML
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
MIT License
|
|
200
|
+
|
|
201
|
+
## Repository
|
|
202
|
+
|
|
203
|
+
Source code, issues, and updates:
|
|
204
|
+
|
|
205
|
+
- Repository: https://github.com/Pragnath/SeisAug
|
|
206
|
+
- Issues: https://github.com/Pragnath/SeisAug/issues
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MANIFEST.in
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/SeisAug.egg-info/PKG-INFO
|
|
5
|
+
src/SeisAug.egg-info/SOURCES.txt
|
|
6
|
+
src/SeisAug.egg-info/dependency_links.txt
|
|
7
|
+
src/SeisAug.egg-info/entry_points.txt
|
|
8
|
+
src/SeisAug.egg-info/requires.txt
|
|
9
|
+
src/SeisAug.egg-info/top_level.txt
|
|
10
|
+
src/seisaug/__init__.py
|
|
11
|
+
src/seisaug/__main__.py
|
|
12
|
+
src/seisaug/cli.py
|
|
13
|
+
src/seisaug/io.py
|
|
14
|
+
src/seisaug/noise.py
|
|
15
|
+
src/seisaug/plotting.py
|
|
16
|
+
src/seisaug/signal.py
|
|
17
|
+
src/seisaug/validate.py
|
|
18
|
+
tests/test_noise.py
|
|
19
|
+
tests/test_signal.py
|
|
20
|
+
tests/test_validate.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
seisaug
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .noise import (
|
|
2
|
+
add_white_noise,
|
|
3
|
+
add_pink_noise,
|
|
4
|
+
add_monofrequency_noise,
|
|
5
|
+
add_bandpass_noise,
|
|
6
|
+
add_spikes,
|
|
7
|
+
)
|
|
8
|
+
from .signal import time_shift_zero_pad, convolve_with_sine, convolve_with_step
|
|
9
|
+
from .io import read_waveform, read_stead_trace, write_stead_trace
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"add_white_noise",
|
|
13
|
+
"add_pink_noise",
|
|
14
|
+
"add_monofrequency_noise",
|
|
15
|
+
"add_bandpass_noise",
|
|
16
|
+
"add_spikes",
|
|
17
|
+
"time_shift_zero_pad",
|
|
18
|
+
"convolve_with_sine",
|
|
19
|
+
"convolve_with_step",
|
|
20
|
+
"read_waveform",
|
|
21
|
+
"read_stead_trace",
|
|
22
|
+
"write_stead_trace",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
__version__ = "1.2.0"
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def batch_main(args):
|
|
5
|
+
from .pipeline import batch_augment
|
|
6
|
+
|
|
7
|
+
batch_augment(
|
|
8
|
+
input_hdf5=args.input_hdf5,
|
|
9
|
+
input_csv=args.input_csv,
|
|
10
|
+
output_hdf5=args.output_hdf5,
|
|
11
|
+
output_csv=args.output_csv,
|
|
12
|
+
config_path=args.config,
|
|
13
|
+
)
|
|
14
|
+
print(f"Augmented dataset written to: {args.output_hdf5}")
|
|
15
|
+
print(f"Metadata written to: {args.output_csv}")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def validate_main(args):
|
|
19
|
+
from .validate import validate_stead_file
|
|
20
|
+
|
|
21
|
+
issues = validate_stead_file(args.input_hdf5, args.input_csv)
|
|
22
|
+
|
|
23
|
+
if issues:
|
|
24
|
+
print("Validation issues found:")
|
|
25
|
+
for item in issues:
|
|
26
|
+
print(f"- {item}")
|
|
27
|
+
raise SystemExit(1)
|
|
28
|
+
|
|
29
|
+
print("Validation passed.")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def snr_main(args):
|
|
33
|
+
from .plotting import plot_snr_distribution
|
|
34
|
+
|
|
35
|
+
plot_snr_distribution(args.input_csv, outfile=args.output_png)
|
|
36
|
+
print(f"SNR distribution saved to: {args.output_png}")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def grid_main(args):
|
|
40
|
+
from .io import read_stead_trace
|
|
41
|
+
from .plotting import plot_augmentation_grid
|
|
42
|
+
|
|
43
|
+
original, _ = read_stead_trace(args.input_hdf5, args.trace_name)
|
|
44
|
+
|
|
45
|
+
augmented = {}
|
|
46
|
+
for name in args.augmented_names:
|
|
47
|
+
augmented_data, _ = read_stead_trace(args.input_hdf5, name)
|
|
48
|
+
augmented[name] = augmented_data
|
|
49
|
+
|
|
50
|
+
plot_augmentation_grid(original, augmented, outfile=args.output_png)
|
|
51
|
+
print(f"Augmentation grid saved to: {args.output_png}")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def build_parser():
|
|
55
|
+
parser = argparse.ArgumentParser(
|
|
56
|
+
prog="seisaug",
|
|
57
|
+
description="SeisAug command-line tools for seismic augmentation, validation, and plotting.",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
subparsers = parser.add_subparsers(dest="command")
|
|
61
|
+
|
|
62
|
+
batch_parser = subparsers.add_parser(
|
|
63
|
+
"batch",
|
|
64
|
+
help="Batch augment a STEAD-format HDF5+CSV dataset",
|
|
65
|
+
)
|
|
66
|
+
batch_parser.add_argument("--input_hdf5", required=True, help="Input STEAD HDF5 file")
|
|
67
|
+
batch_parser.add_argument("--input_csv", required=True, help="Input STEAD metadata CSV")
|
|
68
|
+
batch_parser.add_argument("--output_hdf5", required=True, help="Output augmented HDF5 file")
|
|
69
|
+
batch_parser.add_argument("--output_csv", required=True, help="Output augmented CSV file")
|
|
70
|
+
batch_parser.add_argument("--config", required=True, help="YAML augmentation config file")
|
|
71
|
+
batch_parser.set_defaults(func=batch_main)
|
|
72
|
+
|
|
73
|
+
validate_parser = subparsers.add_parser(
|
|
74
|
+
"validate",
|
|
75
|
+
help="Validate a STEAD-format HDF5/CSV dataset",
|
|
76
|
+
)
|
|
77
|
+
validate_parser.add_argument("--input_hdf5", required=True, help="Input STEAD HDF5 file")
|
|
78
|
+
validate_parser.add_argument("--input_csv", default=None, help="Optional STEAD metadata CSV")
|
|
79
|
+
validate_parser.set_defaults(func=validate_main)
|
|
80
|
+
|
|
81
|
+
snr_parser = subparsers.add_parser(
|
|
82
|
+
"snr",
|
|
83
|
+
help="Plot SNR distribution from STEAD CSV metadata",
|
|
84
|
+
)
|
|
85
|
+
snr_parser.add_argument("--input_csv", required=True, help="Input STEAD metadata CSV")
|
|
86
|
+
snr_parser.add_argument("--output_png", required=True, help="Output PNG path")
|
|
87
|
+
snr_parser.set_defaults(func=snr_main)
|
|
88
|
+
|
|
89
|
+
grid_parser = subparsers.add_parser(
|
|
90
|
+
"grid",
|
|
91
|
+
help="Plot augmentation grid from original and augmented traces",
|
|
92
|
+
)
|
|
93
|
+
grid_parser.add_argument("--input_hdf5", required=True, help="Input STEAD HDF5 file")
|
|
94
|
+
grid_parser.add_argument("--trace_name", required=True, help="Original trace name")
|
|
95
|
+
grid_parser.add_argument(
|
|
96
|
+
"--augmented_names",
|
|
97
|
+
nargs="+",
|
|
98
|
+
required=True,
|
|
99
|
+
help="One or more augmented trace names",
|
|
100
|
+
)
|
|
101
|
+
grid_parser.add_argument("--output_png", required=True, help="Output PNG path")
|
|
102
|
+
grid_parser.set_defaults(func=grid_main)
|
|
103
|
+
|
|
104
|
+
return parser
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def main(argv=None):
|
|
108
|
+
parser = build_parser()
|
|
109
|
+
args = parser.parse_args(argv)
|
|
110
|
+
|
|
111
|
+
if not hasattr(args, "func"):
|
|
112
|
+
parser.print_help()
|
|
113
|
+
raise SystemExit(0)
|
|
114
|
+
|
|
115
|
+
args.func(args)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import h5py
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from obspy import read
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def read_waveform(path):
|
|
8
|
+
return read(str(path))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def read_stead_trace(hdf5_path, trace_name):
|
|
12
|
+
with h5py.File(hdf5_path, "r") as f:
|
|
13
|
+
ds = f[f"data/{trace_name}"]
|
|
14
|
+
data = np.array(ds)
|
|
15
|
+
attrs = {k: ds.attrs[k] for k in ds.attrs.keys()}
|
|
16
|
+
return data, attrs
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def read_stead_metadata(csv_path):
|
|
20
|
+
return pd.read_csv(csv_path)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def write_stead_trace(hdf5_path, trace_name, data, attrs=None, compression="gzip"):
|
|
24
|
+
attrs = attrs or {}
|
|
25
|
+
with h5py.File(hdf5_path, "a") as f:
|
|
26
|
+
grp = f.require_group("data")
|
|
27
|
+
if trace_name in grp:
|
|
28
|
+
del grp[trace_name]
|
|
29
|
+
ds = grp.create_dataset(trace_name, data=np.asarray(data, dtype=np.float32), compression=compression)
|
|
30
|
+
for k, v in attrs.items():
|
|
31
|
+
ds.attrs[k] = v
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def write_stead_metadata(csv_path, rows):
|
|
35
|
+
pd.DataFrame(rows).to_csv(csv_path, index=False)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy import signal
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _db_to_amplitude_ratio(noise_level_db: float) -> float:
|
|
6
|
+
return 10 ** (noise_level_db / 20.0)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def add_white_noise(data, noise_level_db):
|
|
10
|
+
data = np.asarray(data, dtype=np.float32)
|
|
11
|
+
ratio = _db_to_amplitude_ratio(noise_level_db)
|
|
12
|
+
noise = np.random.normal(size=data.shape).astype(np.float32)
|
|
13
|
+
noise_std = np.std(noise) or 1.0
|
|
14
|
+
return data + ratio * (noise / noise_std)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def add_pink_noise(data, noise_level_db):
|
|
18
|
+
data = np.asarray(data, dtype=np.float32)
|
|
19
|
+
ratio = _db_to_amplitude_ratio(noise_level_db)
|
|
20
|
+
n = len(data)
|
|
21
|
+
freqs = np.fft.rfftfreq(n)
|
|
22
|
+
freqs[0] = 1.0
|
|
23
|
+
white = np.fft.rfft(np.random.normal(size=n))
|
|
24
|
+
pink = white / np.sqrt(freqs)
|
|
25
|
+
noise = np.fft.irfft(pink, n=n).astype(np.float32)
|
|
26
|
+
noise_std = np.std(noise) or 1.0
|
|
27
|
+
return data + ratio * (noise / noise_std)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def add_monofrequency_noise(data, noise_level_db, fs, freq):
|
|
31
|
+
data = np.asarray(data, dtype=np.float32)
|
|
32
|
+
ratio = _db_to_amplitude_ratio(noise_level_db)
|
|
33
|
+
t = np.arange(data.shape[0], dtype=np.float32) / float(fs)
|
|
34
|
+
noise = np.sin(2 * np.pi * float(freq) * t).astype(np.float32)
|
|
35
|
+
noise_std = np.std(noise) or 1.0
|
|
36
|
+
return data + ratio * (noise / noise_std)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def add_bandpass_noise(data, noise_level_db, fs, low_freq, high_freq, order=6):
|
|
40
|
+
data = np.asarray(data, dtype=np.float32)
|
|
41
|
+
ratio = _db_to_amplitude_ratio(noise_level_db)
|
|
42
|
+
white = np.random.normal(size=data.shape).astype(np.float32)
|
|
43
|
+
white_std = np.std(white) or 1.0
|
|
44
|
+
white = white / white_std
|
|
45
|
+
sos = signal.butter(order, [low_freq, high_freq], btype="band", fs=fs, output="sos")
|
|
46
|
+
filtered = signal.sosfilt(sos, white).astype(np.float32)
|
|
47
|
+
filt_std = np.std(filtered) or 1.0
|
|
48
|
+
return data + ratio * (filtered / filt_std)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def add_spikes(data, num_spikes=10, spike_amp_ratio=0.3, seed=None):
|
|
52
|
+
data = np.asarray(data, dtype=np.float32).copy()
|
|
53
|
+
rng = np.random.default_rng(seed)
|
|
54
|
+
if len(data) == 0:
|
|
55
|
+
return data
|
|
56
|
+
max_amp = np.max(np.abs(data)) or 1.0
|
|
57
|
+
idx = rng.integers(0, len(data), size=int(num_spikes))
|
|
58
|
+
signs = rng.choice([-1.0, 1.0], size=int(num_spikes))
|
|
59
|
+
data[idx] += signs * float(spike_amp_ratio) * max_amp
|
|
60
|
+
return data
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def plot_trace(data, title="Waveform", outfile=None):
|
|
8
|
+
data = np.asarray(data)
|
|
9
|
+
fig, axes = plt.subplots(3, 1, figsize=(10, 6), sharex=True)
|
|
10
|
+
labels = ["E", "N", "Z"]
|
|
11
|
+
for i, ax in enumerate(axes):
|
|
12
|
+
ax.plot(data[:, i], lw=0.8, color="k")
|
|
13
|
+
ax.set_ylabel(labels[i])
|
|
14
|
+
axes[-1].set_xlabel("Sample")
|
|
15
|
+
fig.suptitle(title)
|
|
16
|
+
fig.tight_layout()
|
|
17
|
+
if outfile:
|
|
18
|
+
fig.savefig(outfile, dpi=200, bbox_inches="tight")
|
|
19
|
+
return fig
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def plot_augmentation_grid(original, augmented_dict, outfile=None):
|
|
23
|
+
n = 1 + len(augmented_dict)
|
|
24
|
+
fig, axes = plt.subplots(n, 3, figsize=(14, 3 * n), sharex=True)
|
|
25
|
+
labels = ["E", "N", "Z"]
|
|
26
|
+
rows = [("original", original)] + list(augmented_dict.items())
|
|
27
|
+
for r, (name, arr) in enumerate(rows):
|
|
28
|
+
arr = np.asarray(arr)
|
|
29
|
+
for c in range(3):
|
|
30
|
+
ax = axes[r, c] if n > 1 else axes[c]
|
|
31
|
+
ax.plot(arr[:, c], lw=0.7, color="k")
|
|
32
|
+
ax.set_title(f"{name} - {labels[c]}")
|
|
33
|
+
if r == n - 1:
|
|
34
|
+
ax.set_xlabel("Sample")
|
|
35
|
+
fig.tight_layout()
|
|
36
|
+
if outfile:
|
|
37
|
+
fig.savefig(outfile, dpi=200, bbox_inches="tight")
|
|
38
|
+
return fig
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def plot_snr_distribution(csv_path, outfile=None):
|
|
42
|
+
df = pd.read_csv(csv_path)
|
|
43
|
+
|
|
44
|
+
def parse_snr(x):
|
|
45
|
+
try:
|
|
46
|
+
vals = ast.literal_eval(str(x))
|
|
47
|
+
vals = np.asarray(vals, dtype=float)
|
|
48
|
+
vals = vals[np.isfinite(vals)]
|
|
49
|
+
vals = vals[vals > -900]
|
|
50
|
+
return float(np.mean(vals)) if len(vals) else np.nan
|
|
51
|
+
except Exception:
|
|
52
|
+
return np.nan
|
|
53
|
+
|
|
54
|
+
df["snr_mean_db"] = df["snr_db"].apply(parse_snr)
|
|
55
|
+
fig, ax = plt.subplots(figsize=(8, 5))
|
|
56
|
+
ax.hist(df["snr_mean_db"].dropna(), bins=40, color="steelblue", alpha=0.8)
|
|
57
|
+
ax.set_xlabel("Mean SNR (dB)")
|
|
58
|
+
ax.set_ylabel("Count")
|
|
59
|
+
ax.set_title("SNR Distribution")
|
|
60
|
+
fig.tight_layout()
|
|
61
|
+
if outfile:
|
|
62
|
+
fig.savefig(outfile, dpi=200, bbox_inches="tight")
|
|
63
|
+
return fig
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def time_shift_zero_pad(data, shift_samples):
|
|
5
|
+
data = np.asarray(data, dtype=np.float32)
|
|
6
|
+
shift_samples = int(shift_samples)
|
|
7
|
+
out = np.zeros_like(data)
|
|
8
|
+
if shift_samples == 0:
|
|
9
|
+
return data.copy()
|
|
10
|
+
if abs(shift_samples) >= len(data):
|
|
11
|
+
return out
|
|
12
|
+
if shift_samples > 0:
|
|
13
|
+
out[shift_samples:] = data[:-shift_samples]
|
|
14
|
+
else:
|
|
15
|
+
out[:shift_samples] = data[-shift_samples:]
|
|
16
|
+
return out
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def convolve_with_sine(data, fs, freq=1.0):
|
|
20
|
+
data = np.asarray(data, dtype=np.float32)
|
|
21
|
+
t = np.arange(len(data), dtype=np.float32) / float(fs)
|
|
22
|
+
sig = np.sin(2 * np.pi * float(freq) * t).astype(np.float32)
|
|
23
|
+
out = np.convolve(sig, data, mode="same").astype(np.float32)
|
|
24
|
+
norm = np.max(np.abs(out)) or 1.0
|
|
25
|
+
return out / norm
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def convolve_with_step(data, fs, num_steps=10):
|
|
29
|
+
data = np.asarray(data, dtype=np.float32)
|
|
30
|
+
sig = np.zeros(len(data), dtype=np.float32)
|
|
31
|
+
edges = np.linspace(0, len(data), int(num_steps) + 1).astype(int)
|
|
32
|
+
for i in range(len(edges) - 1):
|
|
33
|
+
sig[edges[i]:edges[i + 1]] = i % 2
|
|
34
|
+
out = np.convolve(sig, data, mode="same").astype(np.float32)
|
|
35
|
+
norm = np.max(np.abs(out)) or 1.0
|
|
36
|
+
return out / norm
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import h5py
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
REQUIRED_ATTRS = [
|
|
7
|
+
"network_code",
|
|
8
|
+
"receiver_code",
|
|
9
|
+
"receiver_type",
|
|
10
|
+
"trace_name",
|
|
11
|
+
"trace_category",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def validate_stead_file(hdf5_path, csv_path=None):
|
|
16
|
+
issues = []
|
|
17
|
+
|
|
18
|
+
with h5py.File(hdf5_path, "r") as f:
|
|
19
|
+
if "data" not in f:
|
|
20
|
+
issues.append("Missing /data group")
|
|
21
|
+
return issues
|
|
22
|
+
|
|
23
|
+
for trace_name, ds in f["data"].items():
|
|
24
|
+
arr = np.array(ds)
|
|
25
|
+
|
|
26
|
+
if arr.ndim != 2 or arr.shape[1] != 3:
|
|
27
|
+
issues.append(f"{trace_name}: expected shape (N, 3), got {arr.shape}")
|
|
28
|
+
|
|
29
|
+
if not np.all(np.isfinite(arr)):
|
|
30
|
+
issues.append(f"{trace_name}: contains NaN/Inf")
|
|
31
|
+
|
|
32
|
+
for attr in REQUIRED_ATTRS:
|
|
33
|
+
if attr not in ds.attrs:
|
|
34
|
+
issues.append(f"{trace_name}: missing attr {attr}")
|
|
35
|
+
|
|
36
|
+
if csv_path:
|
|
37
|
+
df = pd.read_csv(csv_path)
|
|
38
|
+
if "trace_name" not in df.columns:
|
|
39
|
+
issues.append("CSV missing trace_name column")
|
|
40
|
+
|
|
41
|
+
return issues
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from seisaug.noise import add_white_noise, add_pink_noise, add_spikes
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_add_white_noise_preserves_shape():
|
|
7
|
+
x = np.zeros(1000, dtype=np.float32)
|
|
8
|
+
y = add_white_noise(x, noise_level_db=10)
|
|
9
|
+
assert y.shape == x.shape
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_add_pink_noise_returns_finite_values():
|
|
13
|
+
x = np.zeros(2000, dtype=np.float32)
|
|
14
|
+
y = add_pink_noise(x, noise_level_db=8)
|
|
15
|
+
assert y.shape == x.shape
|
|
16
|
+
assert np.isfinite(y).all()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_add_spikes_preserves_shape_and_changes_samples():
|
|
20
|
+
x = np.ones(1000, dtype=np.float32)
|
|
21
|
+
y = add_spikes(x, num_spikes=5, spike_amp_ratio=0.5, seed=42)
|
|
22
|
+
assert y.shape == x.shape
|
|
23
|
+
assert not np.allclose(x, y)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from seisaug.signal import time_shift_zero_pad, convolve_with_sine, convolve_with_step
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_time_shift_zero_pad_positive_shift():
|
|
7
|
+
x = np.array([1, 2, 3, 4], dtype=np.float32)
|
|
8
|
+
y = time_shift_zero_pad(x, 2)
|
|
9
|
+
assert np.allclose(y, [0, 0, 1, 2])
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_time_shift_zero_pad_negative_shift():
|
|
13
|
+
x = np.array([1, 2, 3, 4], dtype=np.float32)
|
|
14
|
+
y = time_shift_zero_pad(x, -2)
|
|
15
|
+
assert np.allclose(y, [3, 4, 0, 0])
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_convolve_with_sine_shape():
|
|
19
|
+
x = np.random.randn(1000).astype(np.float32)
|
|
20
|
+
y = convolve_with_sine(x, fs=100.0, freq=2.0)
|
|
21
|
+
assert y.shape == x.shape
|
|
22
|
+
assert np.isfinite(y).all()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_convolve_with_step_shape():
|
|
26
|
+
x = np.random.randn(1000).astype(np.float32)
|
|
27
|
+
y = convolve_with_step(x, fs=100.0, num_steps=8)
|
|
28
|
+
assert y.shape == x.shape
|
|
29
|
+
assert np.isfinite(y).all()
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import tempfile
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import h5py
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
from seisaug.validate import validate_stead_file
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_validate_stead_file_passes_for_minimal_valid_input():
|
|
12
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
13
|
+
h5_path = Path(tmpdir) / "test.hdf5"
|
|
14
|
+
csv_path = Path(tmpdir) / "test.csv"
|
|
15
|
+
|
|
16
|
+
with h5py.File(h5_path, "w") as f:
|
|
17
|
+
grp = f.create_group("data")
|
|
18
|
+
ds = grp.create_dataset("trace_1", data=np.zeros((100, 3), dtype=np.float32))
|
|
19
|
+
ds.attrs["network_code"] = "XX"
|
|
20
|
+
ds.attrs["receiver_code"] = "STAT"
|
|
21
|
+
ds.attrs["receiver_type"] = "HH"
|
|
22
|
+
ds.attrs["trace_name"] = "trace_1"
|
|
23
|
+
ds.attrs["trace_category"] = "earthquake_local"
|
|
24
|
+
|
|
25
|
+
pd.DataFrame([{"trace_name": "trace_1"}]).to_csv(csv_path, index=False)
|
|
26
|
+
|
|
27
|
+
issues = validate_stead_file(h5_path, csv_path)
|
|
28
|
+
assert issues == []
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_validate_stead_file_flags_missing_data_group():
|
|
32
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
33
|
+
h5_path = Path(tmpdir) / "bad.hdf5"
|
|
34
|
+
with h5py.File(h5_path, "w"):
|
|
35
|
+
pass
|
|
36
|
+
issues = validate_stead_file(h5_path)
|
|
37
|
+
assert any("Missing /data group" in item for item in issues)
|