pyrregular 0.1.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.
- pyrregular-0.1.0/PKG-INFO +8 -0
- pyrregular-0.1.0/README.md +1 -0
- pyrregular-0.1.0/pyrregular/__init__.py +0 -0
- pyrregular-0.1.0/pyrregular/accessor.py +184 -0
- pyrregular-0.1.0/pyrregular/backend.py +18 -0
- pyrregular-0.1.0/pyrregular/conversion_utils.py +124 -0
- pyrregular-0.1.0/pyrregular/data_utils.py +51 -0
- pyrregular-0.1.0/pyrregular/datasets/__init__.py +0 -0
- pyrregular-0.1.0/pyrregular/datasets/abf.py +180 -0
- pyrregular-0.1.0/pyrregular/datasets/ais.py +92 -0
- pyrregular-0.1.0/pyrregular/datasets/animals.py +62 -0
- pyrregular-0.1.0/pyrregular/datasets/combined_trajectories.py +122 -0
- pyrregular-0.1.0/pyrregular/datasets/garment.py +88 -0
- pyrregular-0.1.0/pyrregular/datasets/geolife.py +176 -0
- pyrregular-0.1.0/pyrregular/datasets/geolife_supervised.py +96 -0
- pyrregular-0.1.0/pyrregular/datasets/ldfpa.py +177 -0
- pyrregular-0.1.0/pyrregular/datasets/mimic3.py +317 -0
- pyrregular-0.1.0/pyrregular/datasets/pamap2.py +129 -0
- pyrregular-0.1.0/pyrregular/datasets/physionet2012.py +177 -0
- pyrregular-0.1.0/pyrregular/datasets/physionet2019.py +102 -0
- pyrregular-0.1.0/pyrregular/datasets/seabirds.py +98 -0
- pyrregular-0.1.0/pyrregular/datasets/t_drive.py +70 -0
- pyrregular-0.1.0/pyrregular/datasets/taxi.py +79 -0
- pyrregular-0.1.0/pyrregular/datasets/vehicles.py +69 -0
- pyrregular-0.1.0/pyrregular/describe.py +66 -0
- pyrregular-0.1.0/pyrregular/dev/__init__.py +0 -0
- pyrregular-0.1.0/pyrregular/dev/repository_dev.py +21 -0
- pyrregular-0.1.0/pyrregular/io_utils.py +270 -0
- pyrregular-0.1.0/pyrregular/reader_interface.py +69 -0
- pyrregular-0.1.0/pyrregular/registry.txt +15 -0
- pyrregular-0.1.0/pyrregular/repository.py +34 -0
- pyrregular-0.1.0/pyrregular/tests/__init__.py +0 -0
- pyrregular-0.1.0/pyrregular/tests/test_dataset_conversion.py +37 -0
- pyrregular-0.1.0/pyrregular/tests/test_dataset_sanity.py +71 -0
- pyrregular-0.1.0/pyrregular/tests/utils.py +39 -0
- pyrregular-0.1.0/pyrregular/uea_ucr_datasets.py +97 -0
- pyrregular-0.1.0/pyrregular/wrappers/__init__.py +0 -0
- pyrregular-0.1.0/pyrregular/wrappers/pypots_wrapper.py +40 -0
- pyrregular-0.1.0/pyrregular.egg-info/PKG-INFO +8 -0
- pyrregular-0.1.0/pyrregular.egg-info/SOURCES.txt +45 -0
- pyrregular-0.1.0/pyrregular.egg-info/dependency_links.txt +1 -0
- pyrregular-0.1.0/pyrregular.egg-info/entry_points.txt +2 -0
- pyrregular-0.1.0/pyrregular.egg-info/requires.txt +15 -0
- pyrregular-0.1.0/pyrregular.egg-info/top_level.txt +1 -0
- pyrregular-0.1.0/setup.cfg +37 -0
- pyrregular-0.1.0/setup.py +4 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# pyrregular
|
|
File without changes
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import xarray as xr
|
|
3
|
+
from pyrregular.conversion_utils import reset_time_index, ak_dropnan, fill_time_index
|
|
4
|
+
import sparse
|
|
5
|
+
import awkward as ak
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@xr.register_dataarray_accessor("irr")
|
|
10
|
+
class IrregularAccessor:
|
|
11
|
+
def __init__(self, da):
|
|
12
|
+
self._da = da
|
|
13
|
+
self.dims = {dim: i for i, dim in enumerate(da.dims)}
|
|
14
|
+
|
|
15
|
+
def __getitem__(self, key):
|
|
16
|
+
import numpy as np
|
|
17
|
+
|
|
18
|
+
out = self._da.__getitem__(key)
|
|
19
|
+
if out["time_id"].size == 1:
|
|
20
|
+
return out
|
|
21
|
+
dims = {dim: i for i, dim in enumerate(out.dims)}
|
|
22
|
+
return out[..., np.sort(out.data.coords[dims["time_id"]])]
|
|
23
|
+
# return out[..., np.sort(out.data.coords[-1])]
|
|
24
|
+
|
|
25
|
+
def get_task(self, task="default"):
|
|
26
|
+
return self._da.attrs["configs"][task]
|
|
27
|
+
|
|
28
|
+
def get_task_target_and_split(self, task="default"):
|
|
29
|
+
task = self.get_task(task)
|
|
30
|
+
return self._da[task["target"]].data, self._da[task["split"]].data
|
|
31
|
+
|
|
32
|
+
def reset_time_index(
|
|
33
|
+
self,
|
|
34
|
+
ts_level=True,
|
|
35
|
+
index_scale=1e-9,
|
|
36
|
+
absolute_time=True,
|
|
37
|
+
concatenate_time=False,
|
|
38
|
+
normalize_time=False,
|
|
39
|
+
):
|
|
40
|
+
return reset_time_index(
|
|
41
|
+
arr=self._da.data,
|
|
42
|
+
time_id=self._da["time_id"].data,
|
|
43
|
+
ts_level=ts_level,
|
|
44
|
+
ts_idx=self.dims["ts_id"],
|
|
45
|
+
signal_idx=self.dims["signal_id"],
|
|
46
|
+
time_idx=self.dims["time_id"],
|
|
47
|
+
index_scale=index_scale,
|
|
48
|
+
absolute_time=absolute_time,
|
|
49
|
+
concatenate_time=concatenate_time,
|
|
50
|
+
normalize_time=normalize_time
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def to_dense(
|
|
54
|
+
self,
|
|
55
|
+
reset_time_index=True,
|
|
56
|
+
ts_level=True,
|
|
57
|
+
index_scale=1e-9,
|
|
58
|
+
absolute_time=True,
|
|
59
|
+
concatenate_time=False,
|
|
60
|
+
normalize_time=False,
|
|
61
|
+
):
|
|
62
|
+
if reset_time_index:
|
|
63
|
+
X, T = self.reset_time_index(
|
|
64
|
+
ts_level=ts_level,
|
|
65
|
+
index_scale=index_scale,
|
|
66
|
+
absolute_time=absolute_time,
|
|
67
|
+
concatenate_time=concatenate_time,
|
|
68
|
+
normalize_time=normalize_time
|
|
69
|
+
)
|
|
70
|
+
else:
|
|
71
|
+
X = self._da.data
|
|
72
|
+
T = self._da["time_id"].data.reshape(1, 1, -1)
|
|
73
|
+
if concatenate_time:
|
|
74
|
+
X = sparse.concatenate([X, T], axis=1)
|
|
75
|
+
return X.todense(), T.todense()
|
|
76
|
+
|
|
77
|
+
def to_tslearn(
|
|
78
|
+
self,
|
|
79
|
+
reset_time_index=True,
|
|
80
|
+
ts_level=True,
|
|
81
|
+
index_scale=1e-9,
|
|
82
|
+
absolute_time=True,
|
|
83
|
+
concatenate_time=False,
|
|
84
|
+
):
|
|
85
|
+
X, T = self.to_dense(
|
|
86
|
+
reset_time_index=reset_time_index,
|
|
87
|
+
ts_level=ts_level,
|
|
88
|
+
index_scale=index_scale,
|
|
89
|
+
absolute_time=absolute_time,
|
|
90
|
+
concatenate_time=concatenate_time,
|
|
91
|
+
)
|
|
92
|
+
return np.swapaxes(X, 1, 2), np.swapaxes(T, 1, 2)
|
|
93
|
+
|
|
94
|
+
def to_aeon(
|
|
95
|
+
self,
|
|
96
|
+
reset_time_index=True,
|
|
97
|
+
ts_level=True,
|
|
98
|
+
index_scale=1e-9,
|
|
99
|
+
absolute_time=True,
|
|
100
|
+
concatenate_time=False,
|
|
101
|
+
):
|
|
102
|
+
X, T = self.to_dense(
|
|
103
|
+
reset_time_index=reset_time_index,
|
|
104
|
+
ts_level=ts_level,
|
|
105
|
+
index_scale=index_scale,
|
|
106
|
+
absolute_time=absolute_time,
|
|
107
|
+
concatenate_time=concatenate_time,
|
|
108
|
+
)
|
|
109
|
+
return X, T
|
|
110
|
+
|
|
111
|
+
def to_sktime(
|
|
112
|
+
self,
|
|
113
|
+
reset_time_index=True,
|
|
114
|
+
ts_level=True,
|
|
115
|
+
index_scale=1e-9,
|
|
116
|
+
absolute_time=True,
|
|
117
|
+
concatenate_time=False,
|
|
118
|
+
):
|
|
119
|
+
X, T = self.to_dense(
|
|
120
|
+
reset_time_index=reset_time_index,
|
|
121
|
+
ts_level=ts_level,
|
|
122
|
+
index_scale=index_scale,
|
|
123
|
+
absolute_time=absolute_time,
|
|
124
|
+
concatenate_time=concatenate_time,
|
|
125
|
+
)
|
|
126
|
+
return X, T
|
|
127
|
+
|
|
128
|
+
def to_awkward(
|
|
129
|
+
self,
|
|
130
|
+
reset_time_index=True,
|
|
131
|
+
ts_level=True,
|
|
132
|
+
index_scale=1e-9,
|
|
133
|
+
absolute_time=True,
|
|
134
|
+
concatenate_time=False,
|
|
135
|
+
dropna=True,
|
|
136
|
+
):
|
|
137
|
+
X, T = self.to_dense(
|
|
138
|
+
reset_time_index=reset_time_index,
|
|
139
|
+
ts_level=ts_level,
|
|
140
|
+
index_scale=index_scale,
|
|
141
|
+
absolute_time=absolute_time,
|
|
142
|
+
concatenate_time=concatenate_time,
|
|
143
|
+
)
|
|
144
|
+
X = ak.Array(X)
|
|
145
|
+
T = ak.Array(T)
|
|
146
|
+
if dropna:
|
|
147
|
+
X = ak_dropnan(X)
|
|
148
|
+
T = ak_dropnan(T)
|
|
149
|
+
return X, T
|
|
150
|
+
|
|
151
|
+
def to_list(
|
|
152
|
+
self,
|
|
153
|
+
reset_time_index=True,
|
|
154
|
+
ts_level=True,
|
|
155
|
+
index_scale=1e-9,
|
|
156
|
+
absolute_time=True,
|
|
157
|
+
concatenate_time=False,
|
|
158
|
+
dropna=True,
|
|
159
|
+
):
|
|
160
|
+
X, T = self.to_awkward(
|
|
161
|
+
reset_time_index=reset_time_index,
|
|
162
|
+
ts_level=ts_level,
|
|
163
|
+
index_scale=index_scale,
|
|
164
|
+
absolute_time=absolute_time,
|
|
165
|
+
concatenate_time=concatenate_time,
|
|
166
|
+
dropna=dropna,
|
|
167
|
+
)
|
|
168
|
+
return X.to_list(), T.to_list()
|
|
169
|
+
|
|
170
|
+
def to_long(self, reset_time_index=True, ts_level=True, index_scale=1e-9, absolute_time=True):
|
|
171
|
+
if reset_time_index:
|
|
172
|
+
X, _ = self.reset_time_index(
|
|
173
|
+
ts_level=ts_level,
|
|
174
|
+
index_scale=index_scale,
|
|
175
|
+
absolute_time=absolute_time,
|
|
176
|
+
concatenate_time=False,
|
|
177
|
+
)
|
|
178
|
+
return np.concatenate([X.coords, X.data[np.newaxis, :]], axis=0).T
|
|
179
|
+
else:
|
|
180
|
+
T = self._da["time_id"].data.astype(np.float_)[self._da.data.coords[self.dims["time_id"]]] * index_scale
|
|
181
|
+
if not absolute_time:
|
|
182
|
+
T = T - T[0]
|
|
183
|
+
return np.concatenate([self._da.data.coords, T[np.newaxis, :]], axis=0).T
|
|
184
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from xarray.backends import BackendEntrypoint
|
|
2
|
+
from pyrregular.io_utils import load_from_file
|
|
3
|
+
from xarray.core.indexing import NdArrayLikeIndexingAdapter
|
|
4
|
+
from pyrregular.accessor import IrregularAccessor # this is needed to register the accessor
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class IrregularEntrypoint(BackendEntrypoint):
|
|
8
|
+
def open_dataset(
|
|
9
|
+
self,
|
|
10
|
+
filename_or_obj,
|
|
11
|
+
*,
|
|
12
|
+
drop_variables=None,
|
|
13
|
+
):
|
|
14
|
+
da = load_from_file(filename_or_obj)
|
|
15
|
+
da.data = NdArrayLikeIndexingAdapter(da.data)
|
|
16
|
+
da.name = "data"
|
|
17
|
+
return da.to_dataset()
|
|
18
|
+
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import numba as nb
|
|
2
|
+
import numpy as np
|
|
3
|
+
import sparse
|
|
4
|
+
import awkward as ak
|
|
5
|
+
from copy import deepcopy
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@nb.njit
|
|
10
|
+
def dense_rank(a):
|
|
11
|
+
unique_values = np.unique(a)
|
|
12
|
+
ranks = np.searchsorted(unique_values, a)
|
|
13
|
+
return ranks
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@nb.njit
|
|
17
|
+
def find_breakpoints(a):
|
|
18
|
+
return np.hstack(
|
|
19
|
+
(np.array([0]), np.argwhere(np.diff(a)).ravel() + 1, np.array([len(a)]))
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@nb.njit
|
|
24
|
+
def find_2d_breakpoints(a, b):
|
|
25
|
+
return np.hstack(
|
|
26
|
+
(
|
|
27
|
+
np.array([0]),
|
|
28
|
+
np.argwhere(np.diff(a) | np.diff(b)).ravel() + 1,
|
|
29
|
+
np.array([len(a)]),
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@nb.njit(parallel=True)
|
|
35
|
+
def remove_fill_values_from_time_idx(
|
|
36
|
+
coords, ts_level=True, ts_idx=0, signal_idx=1, time_idx=-1
|
|
37
|
+
):
|
|
38
|
+
if ts_level:
|
|
39
|
+
breakpoints = find_breakpoints(coords[ts_idx, :])
|
|
40
|
+
else:
|
|
41
|
+
breakpoints = find_2d_breakpoints(coords[ts_idx, :], coords[signal_idx, :])
|
|
42
|
+
out = coords.copy()
|
|
43
|
+
for i in nb.prange(len(breakpoints) - 1):
|
|
44
|
+
start = breakpoints[i]
|
|
45
|
+
end = breakpoints[i + 1]
|
|
46
|
+
dense_time = dense_rank(coords[time_idx, start:end])
|
|
47
|
+
out[time_idx, start:end] = dense_time
|
|
48
|
+
return out
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def reset_time_index(arr: sparse.COO, time_id: np.ndarray, ts_level=True, ts_idx=0, signal_idx=1, time_idx=-1,
|
|
52
|
+
index_scale=1e-9, absolute_time=True, concatenate_time=False, normalize_time=False):
|
|
53
|
+
new_coords = remove_fill_values_from_time_idx(
|
|
54
|
+
arr.coords,
|
|
55
|
+
ts_level=ts_level,
|
|
56
|
+
ts_idx=ts_idx,
|
|
57
|
+
signal_idx=signal_idx,
|
|
58
|
+
time_idx=time_idx,
|
|
59
|
+
)
|
|
60
|
+
new_time_idx = sparse.COO(
|
|
61
|
+
coords=new_coords,
|
|
62
|
+
data=time_id.astype(np.float_)[arr.coords[time_idx]] * index_scale,
|
|
63
|
+
fill_value=arr.fill_value,
|
|
64
|
+
)
|
|
65
|
+
if ts_level:
|
|
66
|
+
new_time_idx = sparse.nanmax(new_time_idx, axis=signal_idx, keepdims=True)
|
|
67
|
+
if not absolute_time:
|
|
68
|
+
new_time_idx = new_time_idx - new_time_idx[:, :, 0:1]
|
|
69
|
+
if normalize_time:
|
|
70
|
+
abs_mean = sparse.nanmean(new_time_idx)
|
|
71
|
+
new_time_idx = (new_time_idx - sparse.nanmin(new_time_idx, axis=2, keepdims=True))
|
|
72
|
+
new_time_idx = new_time_idx / (sparse.nanmax(new_time_idx, axis=2, keepdims=True) + (abs_mean * 1e-8)) # avoids divisions by 0 when there is only 1 timestamp
|
|
73
|
+
if concatenate_time:
|
|
74
|
+
return sparse.concatenate(
|
|
75
|
+
[sparse.COO(coords=new_coords, data=arr.data, fill_value=arr.fill_value), new_time_idx], axis=1
|
|
76
|
+
), new_time_idx
|
|
77
|
+
else:
|
|
78
|
+
return (
|
|
79
|
+
sparse.COO(coords=new_coords, data=arr.data, fill_value=arr.fill_value),
|
|
80
|
+
new_time_idx,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def ak_dropnan(arr, axis=None):
|
|
85
|
+
return ak.drop_none(ak.nan_to_none(arr), axis=axis)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def to_pypots(X, y=None):
|
|
89
|
+
if y is None:
|
|
90
|
+
return dict(
|
|
91
|
+
X=np.swapaxes(X, 1, 2),
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
return dict(
|
|
95
|
+
X=np.swapaxes(X, 1, 2),
|
|
96
|
+
y=y,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def to_tslearn(X):
|
|
101
|
+
return X.swapaxes(1, 2)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def fill_time_index(arr):
|
|
105
|
+
T = deepcopy(arr)
|
|
106
|
+
|
|
107
|
+
# time delta
|
|
108
|
+
a_diff = T[:, :, 1:] - T[:, :, :-1]
|
|
109
|
+
|
|
110
|
+
# mean time delta
|
|
111
|
+
delta_mean = np.nanmean(a_diff, axis=2, keepdims=True)
|
|
112
|
+
|
|
113
|
+
# last timestep
|
|
114
|
+
last_valid_t = np.nanmax(T, axis=2, keepdims=True)
|
|
115
|
+
|
|
116
|
+
# find where the nans are
|
|
117
|
+
nan_mask = np.isnan(T)
|
|
118
|
+
|
|
119
|
+
# where nans are there is an increasing value from 1 to the last nan
|
|
120
|
+
nan_indices = np.cumsum(nan_mask, axis=2)
|
|
121
|
+
|
|
122
|
+
replacement = (last_valid_t + delta_mean * nan_indices)[nan_mask]
|
|
123
|
+
T[nan_mask] = replacement
|
|
124
|
+
return T
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pathlib
|
|
4
|
+
|
|
5
|
+
HUGGINGFACE_REPO_NAME = "pyrregular"
|
|
6
|
+
DATA_ORIGINAL_FOLDER = "data" + "/" + HUGGINGFACE_REPO_NAME + "/" + "data_raw"
|
|
7
|
+
DATA_INTERMEDIATE_FOLDER = "data" + "/" + HUGGINGFACE_REPO_NAME + "/" + "data_mid"
|
|
8
|
+
DATA_FINAL_FOLDER = "data" + "/" + HUGGINGFACE_REPO_NAME + "/" + "data_final"
|
|
9
|
+
METADATA_FOLDER = "metadata"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_project_root() -> pathlib.Path:
|
|
13
|
+
return pathlib.Path(__file__).parent
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def data_original_folder():
|
|
17
|
+
return get_project_root() / ".." / DATA_ORIGINAL_FOLDER
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def data_intermediate_folder():
|
|
21
|
+
return get_project_root() / ".." / DATA_INTERMEDIATE_FOLDER
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def data_final_folder():
|
|
25
|
+
return get_project_root() / ".." / DATA_FINAL_FOLDER
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def metadata_folder():
|
|
29
|
+
return get_project_root() / ".." / METADATA_FOLDER
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def list_final_datasets():
|
|
33
|
+
return sorted(list(data_final_folder().glob("*.h5")))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def list_metadata_files():
|
|
37
|
+
return sorted(list(metadata_folder().glob("*.yml")))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def infer_static_columns(df, id_column, dropna=False):
|
|
41
|
+
df_grouped = df.groupby(id_column).nunique(dropna=dropna)
|
|
42
|
+
return [
|
|
43
|
+
c
|
|
44
|
+
for c in df_grouped.columns
|
|
45
|
+
if np.array_equal(df_grouped[c].to_numpy(), np.ones(len(df_grouped[c])))
|
|
46
|
+
and c != id_column
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
if __name__ == "__main__":
|
|
51
|
+
out = list_final_datasets()
|
|
File without changes
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
from scipy.stats import skewnorm
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
from pyrregular.data_utils import data_original_folder, data_final_folder
|
|
6
|
+
from tqdm.auto import tqdm
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from pyrregular.io_utils import load_yaml, read_csv
|
|
9
|
+
from scipy.stats import skew
|
|
10
|
+
from pyrregular.reader_interface import ReaderInterface
|
|
11
|
+
from xarray import DataArray
|
|
12
|
+
from sklearn.preprocessing import LabelEncoder
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
LABEL_MAP = {0: "bowl", 1: "alembic", 2: "flask"}
|
|
16
|
+
|
|
17
|
+
RANDOM_STATE = 42
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Abf(ReaderInterface):
|
|
21
|
+
fast_to_test = True
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def read_original_version(verbose=False):
|
|
25
|
+
return read_abf(verbose=verbose)
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def _fix_intermediate_version(data: DataArray, verbose=True) -> DataArray:
|
|
29
|
+
split_default = ["train" if i == 1 else "test" for i in data["split"]]
|
|
30
|
+
data = data.assign_coords(split_default=("ts_id", split_default))
|
|
31
|
+
# the following is necessary to make the data compatible with some model benchmarks
|
|
32
|
+
data = data.assign_coords(time_id=("time_id", (data["time_id"].to_numpy() * 100_000_000).astype(int)))
|
|
33
|
+
data = data.rename({"class_default": "class_labels"})
|
|
34
|
+
data = data.rename({"y": "class_default"})
|
|
35
|
+
return data
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _sample_skew_data(a, size):
|
|
39
|
+
return skewnorm.rvs(a, size=size)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _shape_function(n):
|
|
43
|
+
x_values = np.linspace(0, 1, n)
|
|
44
|
+
theta_values = x_values * np.pi
|
|
45
|
+
y_values = -np.sin(theta_values)
|
|
46
|
+
y_values = (y_values - y_values.mean()) / y_values.std()
|
|
47
|
+
return y_values
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _generate_time_instances(instances_per_class, size, skewness):
|
|
51
|
+
values = np.sort(
|
|
52
|
+
[_sample_skew_data(skewness, size) for _ in range(instances_per_class)]
|
|
53
|
+
)
|
|
54
|
+
values = (values - values.min(axis=1, keepdims=True)) / (
|
|
55
|
+
values.max(axis=1, keepdims=True) - values.min(axis=1, keepdims=True)
|
|
56
|
+
)
|
|
57
|
+
return values
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _generate_abf_data(
|
|
61
|
+
instances_per_class=10,
|
|
62
|
+
size=128,
|
|
63
|
+
skewness=10,
|
|
64
|
+
random_state=None,
|
|
65
|
+
noise=True,
|
|
66
|
+
noise_level=0.15,
|
|
67
|
+
n_train_instances_per_class=10,
|
|
68
|
+
):
|
|
69
|
+
np.random.seed(random_state)
|
|
70
|
+
y = _shape_function(n=size)
|
|
71
|
+
y = y.reshape(1, -1)
|
|
72
|
+
Y = np.repeat(y, instances_per_class * 3, axis=0)
|
|
73
|
+
if noise:
|
|
74
|
+
Y += np.random.normal(0, noise_level, size=(instances_per_class * 3, size))
|
|
75
|
+
cylinders_t = _generate_time_instances(instances_per_class, size, 0)
|
|
76
|
+
bells_t = _generate_time_instances(instances_per_class, size, skewness)
|
|
77
|
+
funnels_t = _generate_time_instances(instances_per_class, size, -skewness)
|
|
78
|
+
split_vector = _get_train_test_split(
|
|
79
|
+
instances_per_class * 3, n_train_instances_per_class
|
|
80
|
+
)
|
|
81
|
+
times = np.concatenate((cylinders_t, bells_t, funnels_t))
|
|
82
|
+
skewness_real = skew(times, axis=1)
|
|
83
|
+
return (
|
|
84
|
+
times,
|
|
85
|
+
np.concatenate(
|
|
86
|
+
(
|
|
87
|
+
np.zeros(instances_per_class),
|
|
88
|
+
np.ones(instances_per_class),
|
|
89
|
+
np.full(instances_per_class, 2),
|
|
90
|
+
)
|
|
91
|
+
),
|
|
92
|
+
Y,
|
|
93
|
+
skewness_real,
|
|
94
|
+
split_vector,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _generate_long_abf(
|
|
99
|
+
instances_per_class=10,
|
|
100
|
+
size=128,
|
|
101
|
+
skewness=10,
|
|
102
|
+
random_state=None,
|
|
103
|
+
noise=True,
|
|
104
|
+
noise_level=0.15,
|
|
105
|
+
):
|
|
106
|
+
return _abf_to_long(
|
|
107
|
+
*_generate_abf_data(
|
|
108
|
+
instances_per_class, size, skewness, random_state, noise, noise_level
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _save_long_icbf(
|
|
114
|
+
instances_per_class=10,
|
|
115
|
+
size=128,
|
|
116
|
+
skewness=10,
|
|
117
|
+
random_state=RANDOM_STATE,
|
|
118
|
+
noise=True,
|
|
119
|
+
noise_level=0.25,
|
|
120
|
+
):
|
|
121
|
+
df = _generate_long_abf(
|
|
122
|
+
instances_per_class, size, skewness, random_state, noise, noise_level
|
|
123
|
+
)
|
|
124
|
+
df.to_csv(data_original_folder() / "alembics_bowls_flasks" / "abf_long.csv", index=False)
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _abf_to_long(t, y, X, skewness, split_vector):
|
|
129
|
+
df = pd.DataFrame()
|
|
130
|
+
for i in tqdm(range(X.shape[0])):
|
|
131
|
+
for j in range(X.shape[1]):
|
|
132
|
+
df = pd.concat(
|
|
133
|
+
[
|
|
134
|
+
df,
|
|
135
|
+
pd.DataFrame(
|
|
136
|
+
{
|
|
137
|
+
"time_id": t[i, j],
|
|
138
|
+
"y": int(y[i]),
|
|
139
|
+
"value": X[i, j],
|
|
140
|
+
"class_default": LABEL_MAP[y[i]],
|
|
141
|
+
"skewness": skewness[i],
|
|
142
|
+
"ts_id": str(i),
|
|
143
|
+
"signal_id": str(0),
|
|
144
|
+
"split": split_vector[i],
|
|
145
|
+
},
|
|
146
|
+
index=[0],
|
|
147
|
+
),
|
|
148
|
+
]
|
|
149
|
+
)
|
|
150
|
+
return df
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _get_train_test_split(n_instances, n_instances_train_per_class=10):
|
|
154
|
+
starting_idxs = np.arange(0, n_instances, n_instances // 3)
|
|
155
|
+
split_vector = np.zeros(n_instances, dtype=int)
|
|
156
|
+
for idx in starting_idxs:
|
|
157
|
+
split_vector[idx : idx + n_instances_train_per_class] = 1
|
|
158
|
+
return split_vector
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def read_abf(verbose=False):
|
|
162
|
+
return read_csv(
|
|
163
|
+
filenames=[data_original_folder() / "alembics_bowls_flasks/abf_long.csv"],
|
|
164
|
+
ts_id="ts_id",
|
|
165
|
+
time_id="time_id",
|
|
166
|
+
signal_id="signal_id",
|
|
167
|
+
value_id="value",
|
|
168
|
+
dims={
|
|
169
|
+
"ts_id": ["y", "class_default", "split", "skewness"],
|
|
170
|
+
"signal_id": [],
|
|
171
|
+
"time_id": [],
|
|
172
|
+
},
|
|
173
|
+
verbose=verbose,
|
|
174
|
+
time_index_as_datetime=False,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
if __name__ == "__main__":
|
|
179
|
+
# Abf.save_fixed()
|
|
180
|
+
df = Abf.load_final_version()
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from pyrregular.io_utils import (
|
|
3
|
+
read_csv,
|
|
4
|
+
load_yaml,
|
|
5
|
+
save_to_file,
|
|
6
|
+
load_from_file,
|
|
7
|
+
)
|
|
8
|
+
from pyrregular.data_utils import data_original_folder, data_final_folder
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from xarray import DataArray
|
|
11
|
+
|
|
12
|
+
from pyrregular.data_utils import data_original_folder
|
|
13
|
+
from pyrregular.io_utils import (
|
|
14
|
+
read_csv,
|
|
15
|
+
load_yaml,
|
|
16
|
+
)
|
|
17
|
+
from pyrregular.reader_interface import ReaderInterface
|
|
18
|
+
import numpy as np
|
|
19
|
+
from sklearn.model_selection import train_test_split
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Ais(ReaderInterface):
|
|
23
|
+
@staticmethod
|
|
24
|
+
def read_original_version(verbose=False):
|
|
25
|
+
return read_ais(verbose=verbose)
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def _fix_intermediate_version(data: DataArray, verbose=True) -> DataArray:
|
|
29
|
+
# we use the length of the vessel as the class label (3 sizes)
|
|
30
|
+
labels = np.digitize(data["length"].to_numpy(), bins=[80, 100])
|
|
31
|
+
train_indices, test_indices = train_test_split(
|
|
32
|
+
np.arange(len(data["length"])),
|
|
33
|
+
test_size=0.3,
|
|
34
|
+
stratify=labels,
|
|
35
|
+
random_state=42,
|
|
36
|
+
)
|
|
37
|
+
split = np.array(["train"] * len(data["length"]))
|
|
38
|
+
split[test_indices] = "test"
|
|
39
|
+
|
|
40
|
+
data = data.assign_coords(
|
|
41
|
+
split_default=("ts_id", split), class_default=("ts_id", labels)
|
|
42
|
+
)
|
|
43
|
+
return data
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _dataset_ais(filenames: list):
|
|
47
|
+
# Read the CSV file into a Pandas DataFrame
|
|
48
|
+
for filename in filenames:
|
|
49
|
+
df = pd.read_parquet(filename)
|
|
50
|
+
df["date_time_utc"] = pd.to_datetime(
|
|
51
|
+
df["date_time_utc"], format="%Y-%m-%d %H:%M:%S"
|
|
52
|
+
).apply(
|
|
53
|
+
lambda x: int(x.timestamp())
|
|
54
|
+
) # FIXME
|
|
55
|
+
for i in range(len(df)):
|
|
56
|
+
row = (
|
|
57
|
+
df.iloc[i : i + 1]
|
|
58
|
+
.melt(id_vars=["mmsi", "date_time_utc", "imo_nr", "length"])
|
|
59
|
+
.to_dict(orient="records")
|
|
60
|
+
)
|
|
61
|
+
for new_row in row:
|
|
62
|
+
yield new_row
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def read_ais(verbose=False):
|
|
66
|
+
attrs = load_yaml(
|
|
67
|
+
str(
|
|
68
|
+
data_original_folder()
|
|
69
|
+
/ "terrestrial_vessel_automatic_identification_system/attrs.yml"
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
return read_csv(
|
|
73
|
+
filenames=[
|
|
74
|
+
data_original_folder()
|
|
75
|
+
/ "terrestrial_vessel_automatic_identification_system"
|
|
76
|
+
/ f"2020-2_group_{i}"
|
|
77
|
+
for i in range(40)
|
|
78
|
+
],
|
|
79
|
+
ts_id="mmsi",
|
|
80
|
+
time_id="date_time_utc",
|
|
81
|
+
signal_id="variable",
|
|
82
|
+
value_id="value",
|
|
83
|
+
dims={"ts_id": ["imo_nr", "length"], "signal_id": [], "time_id": []},
|
|
84
|
+
reader_fun=_dataset_ais,
|
|
85
|
+
attrs=attrs,
|
|
86
|
+
verbose=verbose,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
# Ais.save_fixed()
|
|
92
|
+
df = Ais.load_final_version()
|