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.
Files changed (46) hide show
  1. pyrregular-0.1.0/PKG-INFO +8 -0
  2. pyrregular-0.1.0/README.md +1 -0
  3. pyrregular-0.1.0/pyrregular/__init__.py +0 -0
  4. pyrregular-0.1.0/pyrregular/accessor.py +184 -0
  5. pyrregular-0.1.0/pyrregular/backend.py +18 -0
  6. pyrregular-0.1.0/pyrregular/conversion_utils.py +124 -0
  7. pyrregular-0.1.0/pyrregular/data_utils.py +51 -0
  8. pyrregular-0.1.0/pyrregular/datasets/__init__.py +0 -0
  9. pyrregular-0.1.0/pyrregular/datasets/abf.py +180 -0
  10. pyrregular-0.1.0/pyrregular/datasets/ais.py +92 -0
  11. pyrregular-0.1.0/pyrregular/datasets/animals.py +62 -0
  12. pyrregular-0.1.0/pyrregular/datasets/combined_trajectories.py +122 -0
  13. pyrregular-0.1.0/pyrregular/datasets/garment.py +88 -0
  14. pyrregular-0.1.0/pyrregular/datasets/geolife.py +176 -0
  15. pyrregular-0.1.0/pyrregular/datasets/geolife_supervised.py +96 -0
  16. pyrregular-0.1.0/pyrregular/datasets/ldfpa.py +177 -0
  17. pyrregular-0.1.0/pyrregular/datasets/mimic3.py +317 -0
  18. pyrregular-0.1.0/pyrregular/datasets/pamap2.py +129 -0
  19. pyrregular-0.1.0/pyrregular/datasets/physionet2012.py +177 -0
  20. pyrregular-0.1.0/pyrregular/datasets/physionet2019.py +102 -0
  21. pyrregular-0.1.0/pyrregular/datasets/seabirds.py +98 -0
  22. pyrregular-0.1.0/pyrregular/datasets/t_drive.py +70 -0
  23. pyrregular-0.1.0/pyrregular/datasets/taxi.py +79 -0
  24. pyrregular-0.1.0/pyrregular/datasets/vehicles.py +69 -0
  25. pyrregular-0.1.0/pyrregular/describe.py +66 -0
  26. pyrregular-0.1.0/pyrregular/dev/__init__.py +0 -0
  27. pyrregular-0.1.0/pyrregular/dev/repository_dev.py +21 -0
  28. pyrregular-0.1.0/pyrregular/io_utils.py +270 -0
  29. pyrregular-0.1.0/pyrregular/reader_interface.py +69 -0
  30. pyrregular-0.1.0/pyrregular/registry.txt +15 -0
  31. pyrregular-0.1.0/pyrregular/repository.py +34 -0
  32. pyrregular-0.1.0/pyrregular/tests/__init__.py +0 -0
  33. pyrregular-0.1.0/pyrregular/tests/test_dataset_conversion.py +37 -0
  34. pyrregular-0.1.0/pyrregular/tests/test_dataset_sanity.py +71 -0
  35. pyrregular-0.1.0/pyrregular/tests/utils.py +39 -0
  36. pyrregular-0.1.0/pyrregular/uea_ucr_datasets.py +97 -0
  37. pyrregular-0.1.0/pyrregular/wrappers/__init__.py +0 -0
  38. pyrregular-0.1.0/pyrregular/wrappers/pypots_wrapper.py +40 -0
  39. pyrregular-0.1.0/pyrregular.egg-info/PKG-INFO +8 -0
  40. pyrregular-0.1.0/pyrregular.egg-info/SOURCES.txt +45 -0
  41. pyrregular-0.1.0/pyrregular.egg-info/dependency_links.txt +1 -0
  42. pyrregular-0.1.0/pyrregular.egg-info/entry_points.txt +2 -0
  43. pyrregular-0.1.0/pyrregular.egg-info/requires.txt +15 -0
  44. pyrregular-0.1.0/pyrregular.egg-info/top_level.txt +1 -0
  45. pyrregular-0.1.0/setup.cfg +37 -0
  46. pyrregular-0.1.0/setup.py +4 -0
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.1
2
+ Name: pyrregular
3
+ Version: 0.1.0
4
+ Author: Francesco Spinnato, Cristiano Landi
5
+ Author-email: francesco.spinnato@di.unipi.it
6
+ Requires-Python: >=3.9
7
+ Provides-Extra: test
8
+ Provides-Extra: notebooks
@@ -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()