tonik 0.0.1__tar.gz → 0.0.2__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.
- tonik-0.0.2/.devcontainer/devcontainer.json +22 -0
- {tonik-0.0.1 → tonik-0.0.2}/.pytest_cache/v/cache/lastfailed +4 -1
- {tonik-0.0.1 → tonik-0.0.2}/PKG-INFO +3 -2
- tonik-0.0.2/docs/img/Thumbs.db +0 -0
- tonik-0.0.2/docs/img/directory_listing.png +0 -0
- tonik-0.0.2/docs/img/lockerroom_output.png +0 -0
- tonik-0.0.2/docs/img/spectrogram1.png +0 -0
- tonik-0.0.2/docs/img/spectrogram2.png +0 -0
- tonik-0.0.2/docs/index.md +0 -0
- tonik-0.0.2/docs/user_guide.md +53 -0
- tonik-0.0.2/examples/.nfs00000001a69e0bbe002cc265 +0 -0
- tonik-0.0.2/examples/tonik_example.ipynb +117 -0
- tonik-0.0.2/mkdocs.yml +5 -0
- {tonik-0.0.1 → tonik-0.0.2}/pyproject.toml +4 -3
- tonik-0.0.2/src/tonik/__init__.py +1 -0
- tonik-0.0.2/src/tonik/lockerroom.py +290 -0
- {tonik-0.0.1 → tonik-0.0.2}/src/tonik/xarray2hdf5.py +1 -2
- tonik-0.0.2/tests/test_lockerroom.py +0 -0
- {tonik-0.0.1 → tonik-0.0.2}/tests/test_xarray2hdf5.py +1 -1
- tonik-0.0.2/tonik.log +0 -0
- tonik-0.0.1/src/tonik/__init__.py +0 -1
- {tonik-0.0.1 → tonik-0.0.2}/.pyproject.toml.un~ +0 -0
- {tonik-0.0.1 → tonik-0.0.2}/.pytest_cache/.gitignore +0 -0
- {tonik-0.0.1 → tonik-0.0.2}/.pytest_cache/CACHEDIR.TAG +0 -0
- {tonik-0.0.1 → tonik-0.0.2}/.pytest_cache/README.md +0 -0
- {tonik-0.0.1 → tonik-0.0.2}/.pytest_cache/v/cache/nodeids +0 -0
- {tonik-0.0.1 → tonik-0.0.2}/.pytest_cache/v/cache/stepwise +0 -0
- {tonik-0.0.1 → tonik-0.0.2}/LICENSE +0 -0
- {tonik-0.0.1 → tonik-0.0.2}/README.md +0 -0
- {tonik-0.0.1 → tonik-0.0.2}/pyproject.toml~ +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
|
2
|
+
// README at: https://github.com/devcontainers/templates/tree/main/src/python
|
|
3
|
+
{
|
|
4
|
+
"name": "Python 3",
|
|
5
|
+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
|
6
|
+
"image": "mcr.microsoft.com/devcontainers/python:1-3.10-bullseye"
|
|
7
|
+
|
|
8
|
+
// Features to add to the dev container. More info: https://containers.dev/features.
|
|
9
|
+
// "features": {},
|
|
10
|
+
|
|
11
|
+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
12
|
+
// "forwardPorts": [],
|
|
13
|
+
|
|
14
|
+
// Use 'postCreateCommand' to run commands after the container is created.
|
|
15
|
+
// "postCreateCommand": "pip3 install --user -r requirements.txt",
|
|
16
|
+
|
|
17
|
+
// Configure tool-specific properties.
|
|
18
|
+
// "customizations": {},
|
|
19
|
+
|
|
20
|
+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
|
21
|
+
// "remoteUser": "root"
|
|
22
|
+
}
|
|
@@ -5,5 +5,8 @@
|
|
|
5
5
|
"lib64/python3.10/site-packages/h5py/tests": true,
|
|
6
6
|
"lib64/python3.10/site-packages/numpy": true,
|
|
7
7
|
"lib64/python3.10/site-packages/pandas": true,
|
|
8
|
-
"lib64/python3.10/site-packages/xarray/tests": true
|
|
8
|
+
"lib64/python3.10/site-packages/xarray/tests": true,
|
|
9
|
+
"tests/test_xarray2hdf5.py::test_xarray2hdf5": true,
|
|
10
|
+
"tests/test_xarray2hdf5.py::test_xarray2hdf5_with_gaps": true,
|
|
11
|
+
"tests/test_xarray2hdf5.py::test_xarray2hdf5_multi_access": true
|
|
9
12
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tonik
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Summary: A collection of tools to integrate with GNS Science's time series classification platform.
|
|
5
|
-
Project-URL: Homepage, https://
|
|
5
|
+
Project-URL: Homepage, https://tsc-tools.github.io/tonik.github.io
|
|
6
6
|
Project-URL: Issues, https://github.com/tsc-tools/tonik/issues
|
|
7
7
|
Author-email: Yannik Behr <y.behr@gns.cri.nz>, Christof Mueller <c.mueller@gns.cri.nz>
|
|
8
8
|
License-File: LICENSE
|
|
@@ -15,6 +15,7 @@ Requires-Dist: h5py
|
|
|
15
15
|
Requires-Dist: netcdf4
|
|
16
16
|
Requires-Dist: pandas
|
|
17
17
|
Requires-Dist: pytest
|
|
18
|
+
Requires-Dist: python-json-logger
|
|
18
19
|
Requires-Dist: xarray
|
|
19
20
|
Description-Content-Type: text/markdown
|
|
20
21
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
## Examples on how to store and retrieve data using lockerroom
|
|
2
|
+
|
|
3
|
+
```python
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
import os
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
import xarray as xr
|
|
9
|
+
from tonik import Locker, LockerRoom
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
A locker room consists of many lockers. Each locker stores data for a single sensor and the locker room groups sensors by, for example, experiment or geographic location. We will start by generating fake spectrogram data.
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
dates = pd.date_range("2024-01-02", freq='10min', periods=288)
|
|
16
|
+
data = np.abs(np.cumsum(np.random.normal(0, 8., len(dates))))
|
|
17
|
+
data = np.tile(data, (10, 1))
|
|
18
|
+
freqs = np.arange(10)
|
|
19
|
+
xrd = xr.Dataset({'spectrogram': xr.DataArray(data, coords=[freqs, dates],
|
|
20
|
+
dims=['frequency', 'datetime'])})
|
|
21
|
+
xrd['spectrogram'].plot()
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
Now we will store the data under two different sites of the same experiment.
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
lr = LockerRoom('test_experiment')
|
|
30
|
+
l1 = lr.get_locker(site='MDR1', location='00', channel='HHZ')
|
|
31
|
+
l2 = lr.get_locker(site='MDR2', location='00', channel='HHZ')
|
|
32
|
+
l1.save(xrd)
|
|
33
|
+
l2.save(xrd)
|
|
34
|
+
lr
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
!tree /tmp/test_experiment
|
|
41
|
+
```
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
Next we want to retrieve the data we just saved. Before retrieving data we have to set the timespan over which we want to retrieve data.
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
lr.starttime = datetime(2024, 1, 2, 18, 0, 0)
|
|
48
|
+
lr.endtime = datetime(2024, 1, 3, 6, 0, 0)
|
|
49
|
+
l = lr.get_locker('MDR2', '00', 'HHZ')
|
|
50
|
+
l('spectrogram').plot()
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+

|
|
File without changes
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"metadata": {},
|
|
6
|
+
"source": [
|
|
7
|
+
"## Examples on how to store and retrieve data using lockerroom"
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"cell_type": "code",
|
|
12
|
+
"execution_count": null,
|
|
13
|
+
"metadata": {},
|
|
14
|
+
"outputs": [],
|
|
15
|
+
"source": [
|
|
16
|
+
"from datetime import datetime\n",
|
|
17
|
+
"import os\n",
|
|
18
|
+
"import numpy as np\n",
|
|
19
|
+
"import pandas as pd\n",
|
|
20
|
+
"import xarray as xr\n",
|
|
21
|
+
"from tonik import Locker, LockerRoom"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"cell_type": "markdown",
|
|
26
|
+
"metadata": {},
|
|
27
|
+
"source": [
|
|
28
|
+
"A locker room consists of many lockers. Each locker stores data for a single sensor and the locker room groups sensors by, for example, experiment or geographic location. We will start by generating fake spectrogram data."
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"cell_type": "code",
|
|
33
|
+
"execution_count": null,
|
|
34
|
+
"metadata": {},
|
|
35
|
+
"outputs": [],
|
|
36
|
+
"source": [
|
|
37
|
+
"dates = pd.date_range(\"2024-01-02\", freq='10min', periods=288)\n",
|
|
38
|
+
"data = np.abs(np.cumsum(np.random.normal(0, 8., len(dates))))\n",
|
|
39
|
+
"data = np.tile(data, (10, 1))\n",
|
|
40
|
+
"freqs = np.arange(10)\n",
|
|
41
|
+
"xrd = xr.Dataset({'spectrogram': xr.DataArray(data, coords=[freqs, dates],\n",
|
|
42
|
+
" dims=['frequency', 'datetime'])})\n",
|
|
43
|
+
"fig = xrd['spectrogram'].plot()"
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"cell_type": "markdown",
|
|
48
|
+
"metadata": {},
|
|
49
|
+
"source": [
|
|
50
|
+
"Now we will store the data under two different sites of the same experiment."
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"cell_type": "code",
|
|
55
|
+
"execution_count": null,
|
|
56
|
+
"metadata": {},
|
|
57
|
+
"outputs": [],
|
|
58
|
+
"source": [
|
|
59
|
+
"lr = LockerRoom('test_experiment')\n",
|
|
60
|
+
"l1 = lr.get_locker(site='MDR1', location='00', channel='HHZ')\n",
|
|
61
|
+
"l2 = lr.get_locker(site='MDR2', location='00', channel='HHZ')\n",
|
|
62
|
+
"l1.save(xrd)\n",
|
|
63
|
+
"l2.save(xrd)\n",
|
|
64
|
+
"lr"
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"cell_type": "code",
|
|
69
|
+
"execution_count": null,
|
|
70
|
+
"metadata": {},
|
|
71
|
+
"outputs": [],
|
|
72
|
+
"source": [
|
|
73
|
+
"!tree /tmp/test_experiment"
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"cell_type": "markdown",
|
|
78
|
+
"metadata": {},
|
|
79
|
+
"source": [
|
|
80
|
+
"Next we want to retrieve the data we just saved. Before retrieving data we have to set the timespan over which we want to retrieve data."
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"cell_type": "code",
|
|
85
|
+
"execution_count": null,
|
|
86
|
+
"metadata": {},
|
|
87
|
+
"outputs": [],
|
|
88
|
+
"source": [
|
|
89
|
+
"lr.starttime = datetime(2024, 1, 2, 18, 0, 0)\n",
|
|
90
|
+
"lr.endtime = datetime(2024, 1, 3, 6, 0, 0)\n",
|
|
91
|
+
"l = lr.get_locker('MDR2', '00', 'HHZ')\n",
|
|
92
|
+
"l('spectrogram').plot()"
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
"metadata": {
|
|
97
|
+
"kernelspec": {
|
|
98
|
+
"display_name": "Python 3",
|
|
99
|
+
"language": "python",
|
|
100
|
+
"name": "python3"
|
|
101
|
+
},
|
|
102
|
+
"language_info": {
|
|
103
|
+
"codemirror_mode": {
|
|
104
|
+
"name": "ipython",
|
|
105
|
+
"version": 3
|
|
106
|
+
},
|
|
107
|
+
"file_extension": ".py",
|
|
108
|
+
"mimetype": "text/x-python",
|
|
109
|
+
"name": "python",
|
|
110
|
+
"nbconvert_exporter": "python",
|
|
111
|
+
"pygments_lexer": "ipython3",
|
|
112
|
+
"version": "3.10.14"
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"nbformat": 4,
|
|
116
|
+
"nbformat_minor": 2
|
|
117
|
+
}
|
tonik-0.0.2/mkdocs.yml
ADDED
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tonik"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.2"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Yannik Behr", email="y.behr@gns.cri.nz" },
|
|
10
10
|
{ name="Christof Mueller", email="c.mueller@gns.cri.nz" }
|
|
@@ -24,9 +24,10 @@ dependencies = [
|
|
|
24
24
|
"pandas",
|
|
25
25
|
"netcdf4",
|
|
26
26
|
"h5netcdf",
|
|
27
|
-
"pytest"
|
|
27
|
+
"pytest",
|
|
28
|
+
"python-json-logger"
|
|
28
29
|
]
|
|
29
30
|
|
|
30
31
|
[project.urls]
|
|
31
|
-
Homepage = "https://
|
|
32
|
+
Homepage = "https://tsc-tools.github.io/tonik.github.io"
|
|
32
33
|
Issues = "https://github.com/tsc-tools/tonik/issues"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .lockerroom import Locker, LockerRoom
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
from datetime import datetime, timedelta
|
|
2
|
+
import logging
|
|
3
|
+
import logging.config
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
import tempfile
|
|
7
|
+
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import xarray as xr
|
|
10
|
+
|
|
11
|
+
from .xarray2hdf5 import xarray2hdf5
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
ERROR_LOG_FILENAME = "tonik.log"
|
|
15
|
+
|
|
16
|
+
LOGGING_CONFIG = {
|
|
17
|
+
"version": 1,
|
|
18
|
+
"disable_existing_loggers": False,
|
|
19
|
+
"formatters": {
|
|
20
|
+
"default": { # The formatter name, it can be anything that I wish
|
|
21
|
+
"format": "%(asctime)s:%(name)s:%(process)d:%(lineno)d " "%(levelname)s %(message)s", # What to add in the message
|
|
22
|
+
"datefmt": "%Y-%m-%d %H:%M:%S", # How to display dates
|
|
23
|
+
},
|
|
24
|
+
"json": { # The formatter name
|
|
25
|
+
"()": "pythonjsonlogger.jsonlogger.JsonFormatter", # The class to instantiate!
|
|
26
|
+
# Json is more complex, but easier to read, display all attributes!
|
|
27
|
+
"format": """
|
|
28
|
+
asctime: %(asctime)s
|
|
29
|
+
created: %(created)f
|
|
30
|
+
filename: %(filename)s
|
|
31
|
+
funcName: %(funcName)s
|
|
32
|
+
levelname: %(levelname)s
|
|
33
|
+
levelno: %(levelno)s
|
|
34
|
+
lineno: %(lineno)d
|
|
35
|
+
message: %(message)s
|
|
36
|
+
module: %(module)s
|
|
37
|
+
msec: %(msecs)d
|
|
38
|
+
name: %(name)s
|
|
39
|
+
pathname: %(pathname)s
|
|
40
|
+
process: %(process)d
|
|
41
|
+
processName: %(processName)s
|
|
42
|
+
relativeCreated: %(relativeCreated)d
|
|
43
|
+
thread: %(thread)d
|
|
44
|
+
threadName: %(threadName)s
|
|
45
|
+
exc_info: %(exc_info)s
|
|
46
|
+
""",
|
|
47
|
+
"datefmt": "%Y-%m-%d %H:%M:%S", # How to display dates
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
"handlers": {
|
|
51
|
+
"logfile": { # The handler name
|
|
52
|
+
"formatter": "json", # Refer to the formatter defined above
|
|
53
|
+
"level": "ERROR", # FILTER: Only ERROR and CRITICAL logs
|
|
54
|
+
"class": "logging.handlers.RotatingFileHandler", # OUTPUT: Which class to use
|
|
55
|
+
"filename": ERROR_LOG_FILENAME, # Param for class above. Defines filename to use, load it from constant
|
|
56
|
+
"backupCount": 2, # Param for class above. Defines how many log files to keep as it grows
|
|
57
|
+
},
|
|
58
|
+
"simple": { # The handler name
|
|
59
|
+
"formatter": "default", # Refer to the formatter defined above
|
|
60
|
+
"class": "logging.StreamHandler", # OUTPUT: Same as above, stream to console
|
|
61
|
+
"stream": "ext://sys.stdout",
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
"loggers": {
|
|
65
|
+
"zizou": { # The name of the logger, this SHOULD match your module!
|
|
66
|
+
"level": "DEBUG", # FILTER: only INFO logs onwards from "tryceratops" logger
|
|
67
|
+
"handlers": [
|
|
68
|
+
"simple", # Refer the handler defined above
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
"root": {
|
|
73
|
+
"level": "ERROR", # FILTER: only INFO logs onwards
|
|
74
|
+
"handlers": [
|
|
75
|
+
"logfile", # Refer the handler defined above
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
logging.config.dictConfig(LOGGING_CONFIG)
|
|
81
|
+
logger = logging.getLogger("__name__")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class LockerRoom:
|
|
85
|
+
"""
|
|
86
|
+
Query computed features
|
|
87
|
+
|
|
88
|
+
:param rootdir: Path to parent directory.
|
|
89
|
+
:type rootdir: str
|
|
90
|
+
:param starttime: Begin of request
|
|
91
|
+
:type starttime: :class:`datetime.datetime`
|
|
92
|
+
:param endtime: Begin of request
|
|
93
|
+
:type endtime: :class:`datetime.datetime`
|
|
94
|
+
|
|
95
|
+
>>> import datetime
|
|
96
|
+
>>> fq = FeatureRequest()
|
|
97
|
+
>>> start = datetime.datetime(2012,1,1,0,0,0)
|
|
98
|
+
>>> end = datetime.datetime(2012,1,2,23,59,59)
|
|
99
|
+
>>> group = 'Whakaari'
|
|
100
|
+
>>> site = 'WIZ'
|
|
101
|
+
>>> chan = 'HHZ'
|
|
102
|
+
>>> fq.group = group
|
|
103
|
+
>>> fq.starttime = start
|
|
104
|
+
>>> fq.endtime = end
|
|
105
|
+
>>> fq.site = site
|
|
106
|
+
>>> fq.channel = chan
|
|
107
|
+
>>> rsam = fq("rsam")
|
|
108
|
+
"""
|
|
109
|
+
def __init__(self, group, rootdir=tempfile.gettempdir(),
|
|
110
|
+
starttime=None, endtime=None):
|
|
111
|
+
self.groupdir = os.path.join(rootdir, group)
|
|
112
|
+
self.lockers = {}
|
|
113
|
+
|
|
114
|
+
def get_locker(self, site, location, channel):
|
|
115
|
+
key = (site, location, channel)
|
|
116
|
+
if key not in self.lockers:
|
|
117
|
+
self.lockers[key] = Locker(site, location, channel, rootdir=self.groupdir)
|
|
118
|
+
return self.lockers[key]
|
|
119
|
+
|
|
120
|
+
def __repr__(self):
|
|
121
|
+
rstr = f"LockerRoom: {self.groupdir}\n"
|
|
122
|
+
for site, location, channel in self.lockers.keys():
|
|
123
|
+
rstr += f"Site: {site}, Location: {location}, Channel: {channel}\n"
|
|
124
|
+
return rstr
|
|
125
|
+
|
|
126
|
+
def get_starttime(self):
|
|
127
|
+
return self.__starttime
|
|
128
|
+
|
|
129
|
+
def set_starttime(self, time):
|
|
130
|
+
if time is None:
|
|
131
|
+
self.__starttime = None
|
|
132
|
+
self.__sdate = None
|
|
133
|
+
return
|
|
134
|
+
self.__starttime = time
|
|
135
|
+
self.__sdate = '{}{:02d}{:02d}'.format(time.year,
|
|
136
|
+
time.month,
|
|
137
|
+
time.day)
|
|
138
|
+
for key, locker in self.lockers.items():
|
|
139
|
+
locker.starttime = time
|
|
140
|
+
|
|
141
|
+
def get_endtime(self):
|
|
142
|
+
return self.__endtime
|
|
143
|
+
|
|
144
|
+
def set_endtime(self, time):
|
|
145
|
+
if time is None:
|
|
146
|
+
self.__endtime = None
|
|
147
|
+
self.__edate = None
|
|
148
|
+
return
|
|
149
|
+
self.__endtime = time
|
|
150
|
+
self.__edate = '{}{:02d}{:02d}'.format(time.year,
|
|
151
|
+
time.month,
|
|
152
|
+
time.day)
|
|
153
|
+
for key, locker in self.lockers.items():
|
|
154
|
+
locker.endtime = time
|
|
155
|
+
|
|
156
|
+
starttime = property(get_starttime, set_starttime)
|
|
157
|
+
endtime = property(get_endtime, set_endtime)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class Locker:
|
|
161
|
+
def __init__(self, site=None, location=None, channel=None,
|
|
162
|
+
rootdir=None, starttime=None, endtime=None,
|
|
163
|
+
interval='10min'):
|
|
164
|
+
|
|
165
|
+
self.site = site
|
|
166
|
+
self.location = location
|
|
167
|
+
self.channel = channel
|
|
168
|
+
self.starttime = starttime
|
|
169
|
+
self.endtime = endtime
|
|
170
|
+
self.rootdir = rootdir
|
|
171
|
+
self.interval = interval
|
|
172
|
+
|
|
173
|
+
def __call__(self, feature, stack_length=None):
|
|
174
|
+
"""
|
|
175
|
+
Request a particular feature
|
|
176
|
+
|
|
177
|
+
:param feature: Feature name
|
|
178
|
+
:type feature: str
|
|
179
|
+
:param stack_length: length of moving average in time
|
|
180
|
+
:type stack_length: str
|
|
181
|
+
|
|
182
|
+
"""
|
|
183
|
+
if self.endtime <= self.starttime:
|
|
184
|
+
raise ValueError('Startime has to be smaller than endtime.')
|
|
185
|
+
|
|
186
|
+
feature = feature.lower()
|
|
187
|
+
filename = os.path.join(self.sitedir, '%s.nc' % feature)
|
|
188
|
+
if not os.path.isfile(filename):
|
|
189
|
+
raise ValueError('Feature {} does not exist.'.format(feature))
|
|
190
|
+
|
|
191
|
+
logger.debug(f"Reading feature {feature} between {self.starttime} and {self.endtime}")
|
|
192
|
+
num_periods = None
|
|
193
|
+
if stack_length is not None:
|
|
194
|
+
valid_stack_units = ['W', 'D', 'H', 'T', 'min', 'S']
|
|
195
|
+
if not re.match(r'\d*\s*(\w*)', stack_length).group(1)\
|
|
196
|
+
in valid_stack_units:
|
|
197
|
+
raise ValueError(
|
|
198
|
+
'Stack length should be one of: {}'.
|
|
199
|
+
format(', '.join(valid_stack_units))
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
if pd.to_timedelta(stack_length) < pd.to_timedelta(self.interval):
|
|
203
|
+
raise ValueError('Stack length {} is less than interval {}'.
|
|
204
|
+
format(stack_length, self.interval))
|
|
205
|
+
|
|
206
|
+
# Rewind starttime to account for stack length
|
|
207
|
+
self.starttime -= pd.to_timedelta(stack_length)
|
|
208
|
+
|
|
209
|
+
num_periods = (pd.to_timedelta(stack_length)/
|
|
210
|
+
pd.to_timedelta(self.interval))
|
|
211
|
+
if not num_periods.is_integer():
|
|
212
|
+
raise ValueError(
|
|
213
|
+
'Stack length {} / interval {} = {}, but it needs'
|
|
214
|
+
' to be a whole number'.
|
|
215
|
+
format(stack_length, self.interval, num_periods))
|
|
216
|
+
|
|
217
|
+
xd_index = dict(datetime=slice(self.starttime,
|
|
218
|
+
(self.endtime-
|
|
219
|
+
pd.to_timedelta(self.interval))))
|
|
220
|
+
with xr.open_dataset(filename, group='original', engine='h5netcdf') as ds:
|
|
221
|
+
ds.sortby("datetime")
|
|
222
|
+
rq = ds.loc[xd_index].load()
|
|
223
|
+
|
|
224
|
+
# Stack features
|
|
225
|
+
if stack_length is not None:
|
|
226
|
+
logger.debug("Stacking feature...")
|
|
227
|
+
try:
|
|
228
|
+
xdf = rq[feature].rolling(datetime=int(num_periods),
|
|
229
|
+
center=False,
|
|
230
|
+
min_periods=1).mean()
|
|
231
|
+
# Return requested timeframe to that defined in initialisation
|
|
232
|
+
self.starttime += pd.to_timedelta(stack_length)
|
|
233
|
+
xdf_new = xdf.loc[
|
|
234
|
+
self.starttime:
|
|
235
|
+
self.endtime-pd.to_timedelta(self.interval)]
|
|
236
|
+
xdf_new = xdf_new.rename(feature)
|
|
237
|
+
except ValueError as e:
|
|
238
|
+
logger.error(e)
|
|
239
|
+
logger.error('Stack length {} is not valid for feature {}'.
|
|
240
|
+
format(stack_length, feature))
|
|
241
|
+
else:
|
|
242
|
+
return xdf_new
|
|
243
|
+
|
|
244
|
+
return rq[feature]
|
|
245
|
+
|
|
246
|
+
def get_site(self):
|
|
247
|
+
return self.__site
|
|
248
|
+
|
|
249
|
+
def set_site(self, value):
|
|
250
|
+
self.__site = value
|
|
251
|
+
|
|
252
|
+
def get_location(self):
|
|
253
|
+
return self.__location
|
|
254
|
+
|
|
255
|
+
def set_location(self, value):
|
|
256
|
+
self.__location = value
|
|
257
|
+
|
|
258
|
+
def get_channel(self):
|
|
259
|
+
return self.__channel
|
|
260
|
+
|
|
261
|
+
def set_channel(self, value):
|
|
262
|
+
self.__channel = value
|
|
263
|
+
|
|
264
|
+
@property
|
|
265
|
+
def sitedir(self):
|
|
266
|
+
try:
|
|
267
|
+
__sdir = os.path.join(self.rootdir,
|
|
268
|
+
self.site,
|
|
269
|
+
self.location,
|
|
270
|
+
self.channel)
|
|
271
|
+
os.makedirs(__sdir, exist_ok=True)
|
|
272
|
+
return __sdir
|
|
273
|
+
except TypeError:
|
|
274
|
+
return None
|
|
275
|
+
|
|
276
|
+
site = property(get_site, set_site)
|
|
277
|
+
location = property(get_location, set_location)
|
|
278
|
+
channel = property(get_channel, set_channel)
|
|
279
|
+
|
|
280
|
+
def load(self, *args, **kwargs):
|
|
281
|
+
"""
|
|
282
|
+
Load a feature from disk
|
|
283
|
+
"""
|
|
284
|
+
self.__call__(*args, **kwargs)
|
|
285
|
+
|
|
286
|
+
def save(self, data):
|
|
287
|
+
"""
|
|
288
|
+
Save a feature to disk
|
|
289
|
+
"""
|
|
290
|
+
xarray2hdf5(data, self.sitedir)
|
|
@@ -3,10 +3,9 @@ import logging
|
|
|
3
3
|
import os
|
|
4
4
|
from warnings import filterwarnings
|
|
5
5
|
|
|
6
|
-
from cftime import num2date, date2num
|
|
6
|
+
from cftime import num2date, date2num
|
|
7
7
|
import h5netcdf
|
|
8
8
|
import numpy as np
|
|
9
|
-
import xarray as xr
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
def xarray2hdf5(xArray, fdir, rootGroupName="original", timedim="datetime"):
|
|
File without changes
|
tonik-0.0.2/tonik.log
ADDED
|
File without changes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .xarray2hdf5 import xarray2hdf5
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|