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.
@@ -0,0 +1,5 @@
1
+ work_in_progress
2
+ dist
3
+ .pytest_cache
4
+ src/trimes/__pycache__
5
+ tests/__pycache__
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,3 @@
1
+ from .base import *
2
+ from. import regression
3
+
@@ -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
+