trimes 0.0.1__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.
- trimes-0.0.1/.gitignore +5 -0
- trimes-0.0.1/LICENSE +21 -0
- trimes-0.0.1/PKG-INFO +51 -0
- trimes-0.0.1/README.md +13 -0
- trimes-0.0.1/pyproject.toml +28 -0
- trimes-0.0.1/src/trimes/__init__.py +3 -0
- trimes-0.0.1/src/trimes/base.py +232 -0
- trimes-0.0.1/src/trimes/regression.py +29 -0
- trimes-0.0.1/tutorials/getting_started.ipynb +958 -0
trimes-0.0.1/.gitignore
ADDED
trimes-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 FraunhIEE-UniKassel-PowSysStability
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
trimes-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: trimes
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A python package for transient time series
|
|
5
|
+
Project-URL: Homepage, https://github.com/FraunhIEE-UniKassel-PowSysStability/trimes
|
|
6
|
+
Project-URL: Bug Tracker, https://github.com/FraunhIEE-UniKassel-PowSysStability/trimes/issues
|
|
7
|
+
Author-email: Sciemon <simon.eberlein@gmx.de>
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2024 FraunhIEE-UniKassel-PowSysStability
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
31
|
+
Classifier: Operating System :: OS Independent
|
|
32
|
+
Classifier: Programming Language :: Python :: 3
|
|
33
|
+
Requires-Python: >=3.7
|
|
34
|
+
Requires-Dist: matplotlib>1
|
|
35
|
+
Requires-Dist: numpy>1
|
|
36
|
+
Requires-Dist: pandas>2
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# trimes
|
|
40
|
+
|
|
41
|
+
trimes (**tr**ansient t**ime** **s**eries) is a python package for transient time series data in pandas format. The application is actually for all time series data where the time vector has a numerical format (e.g numpy's float64) - as opposed to the frequently used *DateTime* format. To the best of our knowledge, there is currently no other python package focusing on transient time series data as described and the *DateTime* format is not very convenient for transient time series.
|
|
42
|
+
|
|
43
|
+
trimes provides a thin wrapper for pandas *DataFrames* (in the format mentioned above) with helper functions to get data points, for interpolation, resampling, regression etc.
|
|
44
|
+
|
|
45
|
+
The package is at an early stage. Please have a look at the 'getting_started' tutorial.
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```shell
|
|
50
|
+
pip install trimes
|
|
51
|
+
```
|
trimes-0.0.1/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# trimes
|
|
2
|
+
|
|
3
|
+
trimes (**tr**ansient t**ime** **s**eries) is a python package for transient time series data in pandas format. The application is actually for all time series data where the time vector has a numerical format (e.g numpy's float64) - as opposed to the frequently used *DateTime* format. To the best of our knowledge, there is currently no other python package focusing on transient time series data as described and the *DateTime* format is not very convenient for transient time series.
|
|
4
|
+
|
|
5
|
+
trimes provides a thin wrapper for pandas *DataFrames* (in the format mentioned above) with helper functions to get data points, for interpolation, resampling, regression etc.
|
|
6
|
+
|
|
7
|
+
The package is at an early stage. Please have a look at the 'getting_started' tutorial.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```shell
|
|
12
|
+
pip install trimes
|
|
13
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "trimes"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Sciemon", email="simon.eberlein@gmx.de" },
|
|
10
|
+
]
|
|
11
|
+
description = "A python package for transient time series"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
license = { file="LICENSE" }
|
|
14
|
+
requires-python = ">=3.7"
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"pandas > 2",
|
|
22
|
+
"numpy > 1",
|
|
23
|
+
"matplotlib > 1",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
"Homepage" = "https://github.com/FraunhIEE-UniKassel-PowSysStability/trimes"
|
|
28
|
+
"Bug Tracker" = "https://github.com/FraunhIEE-UniKassel-PowSysStability/trimes/issues"
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
from typing import Union
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
from icecream import ic
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_sample(ts: Union[pd.DataFrame, pd.Series],
|
|
9
|
+
time: Union[np.float64, np.array]) -> Union[pd.DataFrame, pd.Series]:
|
|
10
|
+
"""Get first sample after 'time'.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
ts (Union[pd.DataFrame, pd.Series]): time series
|
|
14
|
+
time (np.float64 | np.array): Point(s) in time
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Union[pd.DataFrame, pd.Series]: Sample(s)
|
|
18
|
+
"""
|
|
19
|
+
return ts.iloc[np.searchsorted(ts.index.values, time)] # uses binary search
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_sample_shifted(ts: Union[pd.DataFrame, pd.Series],
|
|
23
|
+
time: np.float64 | list[np.float64],
|
|
24
|
+
shift:int) -> Union[pd.DataFrame, pd.Series]:
|
|
25
|
+
"""Get sample after 'time' but shifted by 'shift' samples.
|
|
26
|
+
|
|
27
|
+
Example: If shift = -1, the first sample after 'time' is searched and then the sample before that is resturned (i.e. the first sample before 'time').
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
ts (Union[pd.DataFrame, pd.Series]): time series
|
|
31
|
+
time (np.float64 | list[np.float64]): point(s) in time
|
|
32
|
+
shift (int): Samples are shifted by 'shift'
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Union[pd.DataFrame, pd.Series]: Sample(s)
|
|
36
|
+
"""
|
|
37
|
+
return ts.iloc[np.searchsorted(ts.index.values, time) + shift]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_samples_around(ts: Union[pd.DataFrame, pd.Series],
|
|
41
|
+
time: np.float64,
|
|
42
|
+
num_samples_before:int = -1,
|
|
43
|
+
num_samples_after:int = 0) -> Union[pd.DataFrame, pd.Series]:
|
|
44
|
+
"""Get samples around a point(s) in time (between 'num_samples_before' and 'num_samples_after', these values are relative to the first sample after 'time'). By default, the samples before and after 'time' are returned.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
ts (Union[pd.DataFrame, pd.Series]): time series
|
|
48
|
+
time (np.float64): point(s) in time
|
|
49
|
+
num_samples_before (int, optional): shift before
|
|
50
|
+
num_samples_after (int, optional): shift after
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
_type_: Sample values
|
|
54
|
+
"""
|
|
55
|
+
index = np.searchsorted(ts.index.values, time)
|
|
56
|
+
return ts.iloc[index+num_samples_before : index+num_samples_after]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_sample_const(df:pd.DataFrame, time: list[np.float64]):
|
|
60
|
+
return df.iloc[np.rint(
|
|
61
|
+
(time - df.index.values[0]) / (df.index.values[1] - df.index.values[0])).astype(int)
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def interp_df(df:pd.DataFrame, time:np.array) -> pd.DataFrame:
|
|
66
|
+
"""Interpolate (linear) DataFrame at 'time'.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
df (pd.DataFrame): time series
|
|
70
|
+
time (np.array): point(s) in time
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
pd.DataFrame: interpolated value(s)
|
|
74
|
+
"""
|
|
75
|
+
return pd.DataFrame(
|
|
76
|
+
interp_np_matrix(time, df.index.to_numpy(), df.to_numpy()),
|
|
77
|
+
columns = df.columns,
|
|
78
|
+
index = time,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def interp_np_matrix(new_x:np.array, old_x:np.array, y: np.matrix) -> np.matrix:
|
|
83
|
+
"""Interpolate (linear) numpy matrix at points 'new_x'.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
new_x (np.array): new x values
|
|
87
|
+
old_x (np.array): old x values
|
|
88
|
+
y (np.matrix): old values
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
np.matrix: Interpolated values
|
|
92
|
+
"""
|
|
93
|
+
interpolated = np.empty((len(new_x),y.shape[1]),dtype=np.float64)
|
|
94
|
+
|
|
95
|
+
for y_col in range(y.shape[1]):
|
|
96
|
+
interpolated[:,y_col] = np.core.multiarray.interp(new_x, old_x, y[:,y_col])
|
|
97
|
+
return interpolated
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def interp_series(ts:pd.Series, time:np.array) -> pd.Series:
|
|
101
|
+
"""Interpolate (linear) time series
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
ts (pd.Series): time series
|
|
105
|
+
time (_type_): point(s) in time
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
pd.Series: Interpolated values
|
|
109
|
+
"""
|
|
110
|
+
return np.core.multiarray.interp(time, ts.index.values, ts.values)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def get_between(ts:Union[pd.DataFrame, pd.Series], tstart:int, tend:int) -> Union[pd.DataFrame, pd.Series]:
|
|
114
|
+
"""Get values between 'tstart' and 'tend'. Returns x for 'tstart' <= x < 'tend'.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
ts (Union[pd.DataFrame, pd.Series]): time series
|
|
118
|
+
tstart (int): start time
|
|
119
|
+
tend (int): end time
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Union[pd.DataFrame, pd.Series]: Values in range
|
|
123
|
+
"""
|
|
124
|
+
indices = np.searchsorted(ts.index.to_numpy(),[tstart, tend])
|
|
125
|
+
return ts.iloc[indices[0]: indices[1]]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def get_index(ts:Union[pd.DataFrame, pd.Series], time:np.array) -> Union[pd.DataFrame, pd.Series]:
|
|
129
|
+
"""Get first index after 'time'.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
ts (Union[pd.DataFrame, pd.Series]): time series
|
|
133
|
+
time (np.array): Point(s) in time.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Union[pd.DataFrame, pd.Series]: index
|
|
137
|
+
"""
|
|
138
|
+
return np.searchsorted(ts.index.values, time)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def resample(ts: Union[pd.DataFrame, pd.Series],
|
|
142
|
+
new_time: Union[np.array, Iterable, float, int]) -> Union[pd.DataFrame, pd.Series]:
|
|
143
|
+
"""Resample time series at 'new_time'.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
ts (Union[pd.DataFrame, pd.Series]): time series
|
|
147
|
+
x (Union[np.array, Iterable, float, int]): New sample time.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Union[pd.DataFrame, pd.Series]: Resampled values
|
|
151
|
+
"""
|
|
152
|
+
if not isinstance(new_time, np.ndarray):
|
|
153
|
+
new_time = np.arange(ts.index.values[0], ts.index.values[-1], new_time)
|
|
154
|
+
return interp_df(ts, new_time)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def get_delta(ts:Union[pd.DataFrame, pd.Series], tstart:np.float64, tend:np.float64):
|
|
158
|
+
"""Get difference between values at 'tend' and 'tstart'
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
ts (Union[pd.DataFrame, pd.Series]): time series
|
|
162
|
+
tstart (np.float64): start time
|
|
163
|
+
tend (np.float64): end time
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
_type_: Difference
|
|
167
|
+
"""
|
|
168
|
+
return get_sample(ts, tend) - get_sample(ts, tstart)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def get_delta_shift(ts: Union[pd.DataFrame, pd.Series],
|
|
172
|
+
time:np.float64,
|
|
173
|
+
num_samples_before:int,
|
|
174
|
+
num_samples_after:int) -> Union[pd.DataFrame, pd.Series]:
|
|
175
|
+
"""Get difference between samples around 'time'. The samples are shifted by 'num_samples_before'/'num_samples_after' indeces (e.g. '0' would be the first sample after 'time').
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
ts (Union[pd.DataFrame, pd.Series]): time series
|
|
179
|
+
time (np.float64): point in time
|
|
180
|
+
num_samples_before (int): sample shift before (usually negative)
|
|
181
|
+
num_samples_after (int): sample shift after (usually positive)
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Union[pd.DataFrame, pd.Series]: Difference
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
index = np.searchsorted(ts.index.values, time)
|
|
188
|
+
return ts.iloc[index+num_samples_after] - ts.iloc[index+num_samples_before]
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def get_delta_around_event(ts:Union[pd.DataFrame, pd.Series],
|
|
192
|
+
evemt_time:np.float64,
|
|
193
|
+
num_samples_before:int = -2,
|
|
194
|
+
num_samples_after:int = 1):
|
|
195
|
+
"""Returns the difference between time series values around an event. Uses 'get_delta_shifted' with default samples. The main reason to have this function in addition to 'get_delta_shifted' is its descriptive name when used in the context of events in time domain simulations.
|
|
196
|
+
The default values (-2 and 1) are chosen because numerical solvers sometimes behave unexpectedly around events. Hence, not the direct samples before and after the event are used, but a distance of one step is maintained.
|
|
197
|
+
"""
|
|
198
|
+
return get_delta_shift(ts, evemt_time, num_samples_before, num_samples_after)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def get_delta_interp_df(df:pd.DataFrame, tstart:np.float64, tend:np.float64) -> pd.DataFrame:
|
|
202
|
+
"""Get difference between interpolated (linear) values at 'tend' and 'tstart'
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
df (pd.DataFrame): time series
|
|
206
|
+
tstart (np.float64): start time
|
|
207
|
+
tend (np.float64): end time
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
pd.DataFrame: Difference
|
|
211
|
+
"""
|
|
212
|
+
values = interp_df(df,(tstart,tend))
|
|
213
|
+
return values.iloc[1,:] - values.iloc[0,:]
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def get_delta_interp_series(s: pd.Series, tstart:int, tend:int):
|
|
217
|
+
"""Get difference between interpolated (linear) values at 'tend' and 'tstart'
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
df (pd.Series): time series
|
|
221
|
+
tstart (np.float64): start time
|
|
222
|
+
tend (np.float64): end time
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
pd.DataFrame: Difference
|
|
226
|
+
"""
|
|
227
|
+
values = interp_series(s,(tstart,tend))
|
|
228
|
+
return values[1] - values[0]
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def polyfit(ts:Union[pd.DataFrame, pd.Series], degree:int, t_offset=None) -> np.array:
|
|
7
|
+
"""Polynomial fit using numpy's 'polyfit'.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
ts (Union[pd.DataFrame, pd.Series]): Values for polynomial fit (x=ts.index, y=ts.to_numpy())
|
|
11
|
+
degree (int): Polynomial
|
|
12
|
+
t_offset (bool, optional): time offset (x=ts.index+t_offset)
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
np.array: Polynomial coeficients
|
|
16
|
+
|
|
17
|
+
It was tried to implement this function using the more recent polynomial approximation as recommended by numpy as shown below. However, there were problems when 'ts' is a DataFrame.
|
|
18
|
+
|
|
19
|
+
if not full:
|
|
20
|
+
return np.polynomial.polynomial.Polynomial.fit(ts.index, ts.to_numpy(), degree).convert().coef
|
|
21
|
+
else:
|
|
22
|
+
return np.polynomial.polynomial.Polynomial.fit(ts.index, ts.to_numpy(), degree)
|
|
23
|
+
"""
|
|
24
|
+
if not t_offset:
|
|
25
|
+
return np.polyfit(ts.index, ts.to_numpy(), degree)
|
|
26
|
+
else:
|
|
27
|
+
return np.polyfit(ts.index+t_offset, ts.to_numpy(), degree)
|
|
28
|
+
|
|
29
|
+
|