timelined_array 0.0.7__tar.gz → 0.0.9__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.
- timelined_array-0.0.9/PKG-INFO +202 -0
- timelined_array-0.0.9/README.md +192 -0
- {timelined_array-0.0.7 → timelined_array-0.0.9}/pyproject.toml +1 -1
- {timelined_array-0.0.7 → timelined_array-0.0.9}/src/timelined_array/__init__.py +1 -1
- {timelined_array-0.0.7 → timelined_array-0.0.9}/src/timelined_array/time.py +42 -53
- timelined_array-0.0.7/PKG-INFO +0 -97
- timelined_array-0.0.7/README.md +0 -87
- {timelined_array-0.0.7 → timelined_array-0.0.9}/LICENSE +0 -0
- {timelined_array-0.0.7 → timelined_array-0.0.9}/tests/__init__.py +0 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: timelined_array
|
|
3
|
+
Version: 0.0.9
|
|
4
|
+
Summary: Manage easily 1 or multidimensionnal samples numpy arrays that are time related. Extends numpy without removing any of it's abilities on such arrays.
|
|
5
|
+
Author-Email: Timothe Jost <timothe.jost@wanadoo.fr>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: numpy
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# timelined_array
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
The TimelinedArray package provides a set of classes and utilities for working with time-indexed arrays. It extends the functionality of NumPy arrays to include time-based indexing and operations, making it easier to work with time-series data.
|
|
15
|
+
|
|
16
|
+
## Classes
|
|
17
|
+
### Timeline
|
|
18
|
+
A subclass of np.ndarray that represents a timeline associated with the array. It includes methods for creating uniformly spaced timelines and calculating time steps.
|
|
19
|
+
|
|
20
|
+
### Boundary
|
|
21
|
+
An enumeration that defines inclusive and exclusive boundaries for time indexing.
|
|
22
|
+
|
|
23
|
+
### TimeIndexer
|
|
24
|
+
A class that provides methods for converting time values to array indices and for indexing arrays based on time.
|
|
25
|
+
|
|
26
|
+
### TimeMixin
|
|
27
|
+
A mixin class that adds time-related methods and properties to arrays, including methods for aligning, transposing, and moving axes.
|
|
28
|
+
|
|
29
|
+
### TimePacker
|
|
30
|
+
A class for packing arrays with their associated timelines. Mostly usefull to plot data fast to matplotlib in case of 1D TimelinedArrays (x and y are unpacked directly from Timeline and the array, into `plt.plot` using `plt.plot(*time_arrray.pack)` )
|
|
31
|
+
|
|
32
|
+
### TimelinedArray
|
|
33
|
+
A subclass of np.ndarray that includes a timeline and a time dimension. It provides methods for time-based indexing and operations.
|
|
34
|
+
|
|
35
|
+
### MaskedTimelinedArray
|
|
36
|
+
A subclass of np.ma.MaskedArray that includes a timeline and a time dimension. It provides methods for time-based indexing and operations on masked arrays.
|
|
37
|
+
|
|
38
|
+
### Seconds
|
|
39
|
+
A simple class for converting seconds to array indices based on a given sampling frequency.
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
To install the TimelinedArray package, simply type in your environment activated console :
|
|
43
|
+
```bash
|
|
44
|
+
pip install timelined_array
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The package can be found on PyPI at : https://pypi.org/project/timelined_array/
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
### Imports
|
|
52
|
+
```python
|
|
53
|
+
from timelined_array import TimelinedArray, MaskedTimelinedArray
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Creating a TimelinedArray
|
|
57
|
+
```python
|
|
58
|
+
import numpy as np
|
|
59
|
+
from timelined_array import TimelinedArray
|
|
60
|
+
|
|
61
|
+
data = np.random.rand(100, 10)
|
|
62
|
+
timeline = np.linspace(0, 10, 100)
|
|
63
|
+
timelined_array = TimelinedArray(data, timeline=timeline, time_dimension=0)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Time-based indexing
|
|
67
|
+
Use the `itime` attribute that allows idexing over time (and time only, if you want otherdimensionnal indexing, look below)
|
|
68
|
+
```python
|
|
69
|
+
# Access data at a specific time
|
|
70
|
+
data_at_time = timelined_array.itime[5.0]
|
|
71
|
+
```
|
|
72
|
+
Here we accessed all points in time closest to time unit 5.0. So because our array was shaped (100,10) and time_dimension was 0, we are obtaining an array of 10 elements, and the type of the returned array is a normal np.ndarray (because we selected a single time point on the time_dimension, we lost the timeseries aspect of our data).
|
|
73
|
+
|
|
74
|
+
or to access a span of time :
|
|
75
|
+
```python
|
|
76
|
+
data_at_time = timelined_array.itime[5.0:9.0]
|
|
77
|
+
```
|
|
78
|
+
Note that the slice ``start`` is ``including`` and ``stop`` is ``excluding``, when working with ``itime``.
|
|
79
|
+
|
|
80
|
+
As such, if one wans to get 9.0 time point related data included, he may write :
|
|
81
|
+
```python
|
|
82
|
+
data_at_time = timelined_array.itime[5.0:9.0]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
(Exclusion / Inclusion handling for start and stop are planned to be tuneable in the future, with a syntax close to :)
|
|
86
|
+
```python
|
|
87
|
+
data_at_time = timelined_array.itime(start:"exclude")[5.0:9.0]
|
|
88
|
+
```
|
|
89
|
+
And exclusion settings set at the level of the array for all future usage might also be implemented. (need to be passed down to child arrays too, wich would imply some reworking of the pickling handling)
|
|
90
|
+
|
|
91
|
+
### Pickling timelinedarrays :
|
|
92
|
+
Timelined arrays and their maked counterpart can be pickled without issue, and the timeline and time dimension is kept, by overriding the __reduce__ and __setstate__ methods of numpy arrays.
|
|
93
|
+
|
|
94
|
+
### Mixing non-time and time-based indexing
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
data_at_time = timelined_array[:,2:4].itime[5.0]
|
|
98
|
+
```
|
|
99
|
+
Here, we seleted the whole first dimension (the timed one) and only a span of 2 to 4 on the second dimension. Then, on the returned TimelinedArray, we select only the time_related data closest to point 5.0. This would leave us with an array of shape : (2,) as we lost the time dimension using itime over a single point, and we selected a span of two over the initial second dimension with [:,2:4].
|
|
100
|
+
|
|
101
|
+
Note that the order doesn't matter, as .itime returns an array over wich you can still iterate normally.
|
|
102
|
+
Of course you still need to pay attention to the dimension of the array that the first .itime indexing will yield.
|
|
103
|
+
|
|
104
|
+
For example, doing this removes the time_dimension, wich is first, so to select a span of the second, we should write :
|
|
105
|
+
```python
|
|
106
|
+
data_at_time = timelined_array.itime[5.0][2:4]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
But if we selected a span over time, we should write :
|
|
110
|
+
```python
|
|
111
|
+
data_at_time = timelined_array.itime[5.0:9.0][:, 2:4]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Synchronizing two arrays in time:
|
|
115
|
+
Is as easy as :
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
sync_point = 5.0
|
|
119
|
+
sample_size = 25
|
|
120
|
+
|
|
121
|
+
sync_data_1 = timelined_array_1.itime[5.0:][:,:sample_size]
|
|
122
|
+
sync_data_2 = timelined_array_2.itime[5.0:][:,:sample_size]
|
|
123
|
+
```
|
|
124
|
+
Where sync_point is the start of you new synchronized arrays, and sample_size is the lendth they will both have on the time_dimension. Using such method, you can stack them easily (however, even if understanding that this is possible and how it works is usefull to beter deal with this package, please see the tutorial step called **Synchronizing timelined array to a new higher dimension** for an easier way to perform such stacking on the time dimension after synchronization, and some details on the caveats to avoid.)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
### Performing operations reducing dimensions
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
# Access data at a specific time
|
|
131
|
+
data_at_time = timelined_array.mean(axis = 0)
|
|
132
|
+
```
|
|
133
|
+
This will leave us with a normal np.ndarray as we loose the dimension 0 wich is our time_dimension.
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
# Access data at a specific time
|
|
137
|
+
data_at_time = timelined_array.mean(axis = 1)
|
|
138
|
+
```
|
|
139
|
+
On the other hand, this will leave us with a TimelinedArray, but of only one dimension (the second one collapsed). The timeline didn't change as we didn't touched the time_dimension.
|
|
140
|
+
|
|
141
|
+
For now, the available collapsing/dimension ordering alterating methods are :
|
|
142
|
+
- sum
|
|
143
|
+
- mean
|
|
144
|
+
- std
|
|
145
|
+
- var
|
|
146
|
+
- swapaxes
|
|
147
|
+
- transpose
|
|
148
|
+
- T
|
|
149
|
+
- moveaxis
|
|
150
|
+
- rollaxis
|
|
151
|
+
|
|
152
|
+
Feel free to request more ufunc support if you need it.
|
|
153
|
+
|
|
154
|
+
### Synchronizing timelined array to a new higher dimension
|
|
155
|
+
Say you want to create a higner dimensionnality array, with one timeline, from different arrays (a iterable of them) that all have a timeline that is containing a common part (some of them might start earlier, end later in the timeline, but they all must have at least some space in common)
|
|
156
|
+
This function provides the necessary help to do that. It does so by checking the first common available time in all the arrays in the iterable. Then, from that common first timepoint, it checks up to how many timepoints it can go so that all arrays have the same number of points in the end. (to stack them to a higher dimension, as the new first dimension).
|
|
157
|
+
|
|
158
|
+
Because it it's internal working, as just described above, it has two caveats :
|
|
159
|
+
- it is implied that your timelines must have the same, or almost equivalent, time step between two points in time. This is not enforced for performance reasons, so be aware of what you do and what you work with. If necessary, you might resample your data first to a common time step and then use this method. This resampling might be implemented in a method that is attached to the TimelinedArray (In a close future, not planned at all on the ``MaskedTimelinedArrays`` version of the arrays, as it would imply too complex fillding over the binary mask to choose what should be masked or not, for an ue case i don't need right now). If you want to check the average time step (calculated with a ``.mean`` of the ``.diff`` over the timeline) you have on the timeline of a given array to compare them, you can use ``my_array.timeline.step``. Getting the step_variation (calculated with a ``.std`` of the ``.diff`` over the timeline) may be implemented soon. Feel free to request the feature if you need it.
|
|
160
|
+
- it checks the **first** time point available for all arrays, by performing a simple ``.min()`` on the timeline of all arrays so it implies that all timelines of the arrays in the iterable are rising. For my own purpose, it doesn't happend that arrays that have a decreasing timeline, but an option to state wether the timelines are increasing or decreasing in time might be added in the future to this function (default will be increasing). Feel free to request the feature if you need it.
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
# Aligning arrays
|
|
164
|
+
aligned_array = TimelinedArray.align_from_iterable([timelined_array, another_timelined_array, yet_another_timelined_array])
|
|
165
|
+
```
|
|
166
|
+
In that example, the new higer dimension of size 3 will be the new first dimension, and the time_dimension of the newly created array will thus be shifted of +1 compared to the time_dimension of the original timelined_arrays in the iterable.
|
|
167
|
+
|
|
168
|
+
### Masking TimelinedArrays
|
|
169
|
+
```python
|
|
170
|
+
from timelined_array import MaskedTimelinedArray
|
|
171
|
+
|
|
172
|
+
masked_data = np.ma.masked_array(data, mask=data > 0.5)
|
|
173
|
+
masked_timelined_array = MaskedTimelinedArray(masked_data, timeline=timeline, time_dimension=0)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Converting time location to index location
|
|
177
|
+
This code finds the closest index on the `time_dimension`, to the time point at `5.0` (can be seconds, milliseconds, whatever you prefer that the time value represents for your situation)
|
|
178
|
+
```python
|
|
179
|
+
masked_data.itime.time_to_index(5.0)
|
|
180
|
+
```
|
|
181
|
+
You can also get a bunch of indexes for a set of time points, in one go :
|
|
182
|
+
```python
|
|
183
|
+
masked_data.itime.time_to_index([5.0,8.0,12.0,4.0])
|
|
184
|
+
```
|
|
185
|
+
Note that the set of timepoints doesn't necessarily need to ordered in time in a strictly increasing or decreasing way, allowing you to easily sort an array :
|
|
186
|
+
```python
|
|
187
|
+
sorted_index = masked_data.itime.time_to_index([5.0,8.0,12.0,4.0])
|
|
188
|
+
sorted_masked_data = masked_data[sorted_index]
|
|
189
|
+
```
|
|
190
|
+
By doing so, the timeline is also sorted at the same time than your data. Coherence is respected. But beware that the ``step`` method of the attached timeline will no longer make sense. (It lonly does when an array is strictly increasing or decreasing in time). Because the `_step` value is cached however, it might not complain about it if you request it, and the value will not reflect the reality. Given how specific this issue is, it might not be dealt with soon.
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
## Misc
|
|
194
|
+
|
|
195
|
+
### License
|
|
196
|
+
This package is licensed under the MIT License. See the LICENSE file for more details.
|
|
197
|
+
|
|
198
|
+
### Contributing
|
|
199
|
+
Contributions are welcome! Please submit a pull request or open an issue to discuss any changes.
|
|
200
|
+
|
|
201
|
+
### Contact
|
|
202
|
+
For any questions or issues, please contact the package maintainer.
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# timelined_array
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
The TimelinedArray package provides a set of classes and utilities for working with time-indexed arrays. It extends the functionality of NumPy arrays to include time-based indexing and operations, making it easier to work with time-series data.
|
|
5
|
+
|
|
6
|
+
## Classes
|
|
7
|
+
### Timeline
|
|
8
|
+
A subclass of np.ndarray that represents a timeline associated with the array. It includes methods for creating uniformly spaced timelines and calculating time steps.
|
|
9
|
+
|
|
10
|
+
### Boundary
|
|
11
|
+
An enumeration that defines inclusive and exclusive boundaries for time indexing.
|
|
12
|
+
|
|
13
|
+
### TimeIndexer
|
|
14
|
+
A class that provides methods for converting time values to array indices and for indexing arrays based on time.
|
|
15
|
+
|
|
16
|
+
### TimeMixin
|
|
17
|
+
A mixin class that adds time-related methods and properties to arrays, including methods for aligning, transposing, and moving axes.
|
|
18
|
+
|
|
19
|
+
### TimePacker
|
|
20
|
+
A class for packing arrays with their associated timelines. Mostly usefull to plot data fast to matplotlib in case of 1D TimelinedArrays (x and y are unpacked directly from Timeline and the array, into `plt.plot` using `plt.plot(*time_arrray.pack)` )
|
|
21
|
+
|
|
22
|
+
### TimelinedArray
|
|
23
|
+
A subclass of np.ndarray that includes a timeline and a time dimension. It provides methods for time-based indexing and operations.
|
|
24
|
+
|
|
25
|
+
### MaskedTimelinedArray
|
|
26
|
+
A subclass of np.ma.MaskedArray that includes a timeline and a time dimension. It provides methods for time-based indexing and operations on masked arrays.
|
|
27
|
+
|
|
28
|
+
### Seconds
|
|
29
|
+
A simple class for converting seconds to array indices based on a given sampling frequency.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
To install the TimelinedArray package, simply type in your environment activated console :
|
|
33
|
+
```bash
|
|
34
|
+
pip install timelined_array
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The package can be found on PyPI at : https://pypi.org/project/timelined_array/
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
### Imports
|
|
42
|
+
```python
|
|
43
|
+
from timelined_array import TimelinedArray, MaskedTimelinedArray
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Creating a TimelinedArray
|
|
47
|
+
```python
|
|
48
|
+
import numpy as np
|
|
49
|
+
from timelined_array import TimelinedArray
|
|
50
|
+
|
|
51
|
+
data = np.random.rand(100, 10)
|
|
52
|
+
timeline = np.linspace(0, 10, 100)
|
|
53
|
+
timelined_array = TimelinedArray(data, timeline=timeline, time_dimension=0)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Time-based indexing
|
|
57
|
+
Use the `itime` attribute that allows idexing over time (and time only, if you want otherdimensionnal indexing, look below)
|
|
58
|
+
```python
|
|
59
|
+
# Access data at a specific time
|
|
60
|
+
data_at_time = timelined_array.itime[5.0]
|
|
61
|
+
```
|
|
62
|
+
Here we accessed all points in time closest to time unit 5.0. So because our array was shaped (100,10) and time_dimension was 0, we are obtaining an array of 10 elements, and the type of the returned array is a normal np.ndarray (because we selected a single time point on the time_dimension, we lost the timeseries aspect of our data).
|
|
63
|
+
|
|
64
|
+
or to access a span of time :
|
|
65
|
+
```python
|
|
66
|
+
data_at_time = timelined_array.itime[5.0:9.0]
|
|
67
|
+
```
|
|
68
|
+
Note that the slice ``start`` is ``including`` and ``stop`` is ``excluding``, when working with ``itime``.
|
|
69
|
+
|
|
70
|
+
As such, if one wans to get 9.0 time point related data included, he may write :
|
|
71
|
+
```python
|
|
72
|
+
data_at_time = timelined_array.itime[5.0:9.0]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
(Exclusion / Inclusion handling for start and stop are planned to be tuneable in the future, with a syntax close to :)
|
|
76
|
+
```python
|
|
77
|
+
data_at_time = timelined_array.itime(start:"exclude")[5.0:9.0]
|
|
78
|
+
```
|
|
79
|
+
And exclusion settings set at the level of the array for all future usage might also be implemented. (need to be passed down to child arrays too, wich would imply some reworking of the pickling handling)
|
|
80
|
+
|
|
81
|
+
### Pickling timelinedarrays :
|
|
82
|
+
Timelined arrays and their maked counterpart can be pickled without issue, and the timeline and time dimension is kept, by overriding the __reduce__ and __setstate__ methods of numpy arrays.
|
|
83
|
+
|
|
84
|
+
### Mixing non-time and time-based indexing
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
data_at_time = timelined_array[:,2:4].itime[5.0]
|
|
88
|
+
```
|
|
89
|
+
Here, we seleted the whole first dimension (the timed one) and only a span of 2 to 4 on the second dimension. Then, on the returned TimelinedArray, we select only the time_related data closest to point 5.0. This would leave us with an array of shape : (2,) as we lost the time dimension using itime over a single point, and we selected a span of two over the initial second dimension with [:,2:4].
|
|
90
|
+
|
|
91
|
+
Note that the order doesn't matter, as .itime returns an array over wich you can still iterate normally.
|
|
92
|
+
Of course you still need to pay attention to the dimension of the array that the first .itime indexing will yield.
|
|
93
|
+
|
|
94
|
+
For example, doing this removes the time_dimension, wich is first, so to select a span of the second, we should write :
|
|
95
|
+
```python
|
|
96
|
+
data_at_time = timelined_array.itime[5.0][2:4]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
But if we selected a span over time, we should write :
|
|
100
|
+
```python
|
|
101
|
+
data_at_time = timelined_array.itime[5.0:9.0][:, 2:4]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Synchronizing two arrays in time:
|
|
105
|
+
Is as easy as :
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
sync_point = 5.0
|
|
109
|
+
sample_size = 25
|
|
110
|
+
|
|
111
|
+
sync_data_1 = timelined_array_1.itime[5.0:][:,:sample_size]
|
|
112
|
+
sync_data_2 = timelined_array_2.itime[5.0:][:,:sample_size]
|
|
113
|
+
```
|
|
114
|
+
Where sync_point is the start of you new synchronized arrays, and sample_size is the lendth they will both have on the time_dimension. Using such method, you can stack them easily (however, even if understanding that this is possible and how it works is usefull to beter deal with this package, please see the tutorial step called **Synchronizing timelined array to a new higher dimension** for an easier way to perform such stacking on the time dimension after synchronization, and some details on the caveats to avoid.)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
### Performing operations reducing dimensions
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
# Access data at a specific time
|
|
121
|
+
data_at_time = timelined_array.mean(axis = 0)
|
|
122
|
+
```
|
|
123
|
+
This will leave us with a normal np.ndarray as we loose the dimension 0 wich is our time_dimension.
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
# Access data at a specific time
|
|
127
|
+
data_at_time = timelined_array.mean(axis = 1)
|
|
128
|
+
```
|
|
129
|
+
On the other hand, this will leave us with a TimelinedArray, but of only one dimension (the second one collapsed). The timeline didn't change as we didn't touched the time_dimension.
|
|
130
|
+
|
|
131
|
+
For now, the available collapsing/dimension ordering alterating methods are :
|
|
132
|
+
- sum
|
|
133
|
+
- mean
|
|
134
|
+
- std
|
|
135
|
+
- var
|
|
136
|
+
- swapaxes
|
|
137
|
+
- transpose
|
|
138
|
+
- T
|
|
139
|
+
- moveaxis
|
|
140
|
+
- rollaxis
|
|
141
|
+
|
|
142
|
+
Feel free to request more ufunc support if you need it.
|
|
143
|
+
|
|
144
|
+
### Synchronizing timelined array to a new higher dimension
|
|
145
|
+
Say you want to create a higner dimensionnality array, with one timeline, from different arrays (a iterable of them) that all have a timeline that is containing a common part (some of them might start earlier, end later in the timeline, but they all must have at least some space in common)
|
|
146
|
+
This function provides the necessary help to do that. It does so by checking the first common available time in all the arrays in the iterable. Then, from that common first timepoint, it checks up to how many timepoints it can go so that all arrays have the same number of points in the end. (to stack them to a higher dimension, as the new first dimension).
|
|
147
|
+
|
|
148
|
+
Because it it's internal working, as just described above, it has two caveats :
|
|
149
|
+
- it is implied that your timelines must have the same, or almost equivalent, time step between two points in time. This is not enforced for performance reasons, so be aware of what you do and what you work with. If necessary, you might resample your data first to a common time step and then use this method. This resampling might be implemented in a method that is attached to the TimelinedArray (In a close future, not planned at all on the ``MaskedTimelinedArrays`` version of the arrays, as it would imply too complex fillding over the binary mask to choose what should be masked or not, for an ue case i don't need right now). If you want to check the average time step (calculated with a ``.mean`` of the ``.diff`` over the timeline) you have on the timeline of a given array to compare them, you can use ``my_array.timeline.step``. Getting the step_variation (calculated with a ``.std`` of the ``.diff`` over the timeline) may be implemented soon. Feel free to request the feature if you need it.
|
|
150
|
+
- it checks the **first** time point available for all arrays, by performing a simple ``.min()`` on the timeline of all arrays so it implies that all timelines of the arrays in the iterable are rising. For my own purpose, it doesn't happend that arrays that have a decreasing timeline, but an option to state wether the timelines are increasing or decreasing in time might be added in the future to this function (default will be increasing). Feel free to request the feature if you need it.
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
# Aligning arrays
|
|
154
|
+
aligned_array = TimelinedArray.align_from_iterable([timelined_array, another_timelined_array, yet_another_timelined_array])
|
|
155
|
+
```
|
|
156
|
+
In that example, the new higer dimension of size 3 will be the new first dimension, and the time_dimension of the newly created array will thus be shifted of +1 compared to the time_dimension of the original timelined_arrays in the iterable.
|
|
157
|
+
|
|
158
|
+
### Masking TimelinedArrays
|
|
159
|
+
```python
|
|
160
|
+
from timelined_array import MaskedTimelinedArray
|
|
161
|
+
|
|
162
|
+
masked_data = np.ma.masked_array(data, mask=data > 0.5)
|
|
163
|
+
masked_timelined_array = MaskedTimelinedArray(masked_data, timeline=timeline, time_dimension=0)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Converting time location to index location
|
|
167
|
+
This code finds the closest index on the `time_dimension`, to the time point at `5.0` (can be seconds, milliseconds, whatever you prefer that the time value represents for your situation)
|
|
168
|
+
```python
|
|
169
|
+
masked_data.itime.time_to_index(5.0)
|
|
170
|
+
```
|
|
171
|
+
You can also get a bunch of indexes for a set of time points, in one go :
|
|
172
|
+
```python
|
|
173
|
+
masked_data.itime.time_to_index([5.0,8.0,12.0,4.0])
|
|
174
|
+
```
|
|
175
|
+
Note that the set of timepoints doesn't necessarily need to ordered in time in a strictly increasing or decreasing way, allowing you to easily sort an array :
|
|
176
|
+
```python
|
|
177
|
+
sorted_index = masked_data.itime.time_to_index([5.0,8.0,12.0,4.0])
|
|
178
|
+
sorted_masked_data = masked_data[sorted_index]
|
|
179
|
+
```
|
|
180
|
+
By doing so, the timeline is also sorted at the same time than your data. Coherence is respected. But beware that the ``step`` method of the attached timeline will no longer make sense. (It lonly does when an array is strictly increasing or decreasing in time). Because the `_step` value is cached however, it might not complain about it if you request it, and the value will not reflect the reality. Given how specific this issue is, it might not be dealt with soon.
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
## Misc
|
|
184
|
+
|
|
185
|
+
### License
|
|
186
|
+
This package is licensed under the MIT License. See the LICENSE file for more details.
|
|
187
|
+
|
|
188
|
+
### Contributing
|
|
189
|
+
Contributions are welcome! Please submit a pull request or open an issue to discuss any changes.
|
|
190
|
+
|
|
191
|
+
### Contact
|
|
192
|
+
For any questions or issues, please contact the package maintainer.
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
from logging import getLogger
|
|
5
|
-
from typing import Tuple, List, Protocol, Type
|
|
6
5
|
from enum import Enum
|
|
7
6
|
import operator
|
|
8
7
|
|
|
8
|
+
from typing import Tuple, List, Protocol, Type, Callable, Any
|
|
9
|
+
|
|
10
|
+
OperatorType = Callable[[Any, Any], bool]
|
|
11
|
+
|
|
9
12
|
# class syntax
|
|
10
13
|
|
|
11
14
|
logger = getLogger("timelined_array")
|
|
@@ -33,8 +36,6 @@ class TimeCompatibleProtocol(Protocol):
|
|
|
33
36
|
|
|
34
37
|
def transpose(self): ...
|
|
35
38
|
|
|
36
|
-
def _finish_axis_removing_operation(self, result, axis): ...
|
|
37
|
-
|
|
38
39
|
|
|
39
40
|
class Timeline(np.ndarray):
|
|
40
41
|
_step = None
|
|
@@ -151,56 +152,39 @@ class Timeline(np.ndarray):
|
|
|
151
152
|
return self._max_step * self.max_step_mult
|
|
152
153
|
|
|
153
154
|
|
|
154
|
-
class
|
|
155
|
-
inclusive =
|
|
156
|
-
exclusive =
|
|
157
|
-
inc =
|
|
158
|
-
exc =
|
|
155
|
+
class StartBoundary(Enum):
|
|
156
|
+
inclusive = operator.ge
|
|
157
|
+
exclusive = operator.gt
|
|
158
|
+
inc = operator.ge
|
|
159
|
+
exc = operator.gt
|
|
159
160
|
|
|
160
|
-
@staticmethod
|
|
161
|
-
def get_operation(boundary, setting):
|
|
162
|
-
"""Return the appropriate comparison operator based on the boundary and setting.
|
|
163
161
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
162
|
+
class StoptBoundary(Enum):
|
|
163
|
+
inclusive = operator.le
|
|
164
|
+
exclusive = operator.lt
|
|
165
|
+
inc = operator.le
|
|
166
|
+
exc = operator.lt
|
|
167
167
|
|
|
168
|
-
Returns:
|
|
169
|
-
function: The comparison operator based on the boundary and setting.
|
|
170
168
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if boundary == "start":
|
|
176
|
-
if setting == Boundary.inclusive:
|
|
177
|
-
return operator.ge
|
|
178
|
-
elif setting == Boundary.exclusive:
|
|
179
|
-
return operator.gt
|
|
180
|
-
else:
|
|
181
|
-
raise ValueError
|
|
182
|
-
elif boundary == "stop":
|
|
183
|
-
if setting == Boundary.inclusive:
|
|
184
|
-
return operator.le
|
|
185
|
-
elif setting == Boundary.exclusive:
|
|
186
|
-
return operator.lt
|
|
187
|
-
else:
|
|
188
|
-
raise ValueError
|
|
189
|
-
else:
|
|
190
|
-
raise ValueError
|
|
169
|
+
class EdgePolicy(Enum):
|
|
170
|
+
start = StartBoundary
|
|
171
|
+
stop = StoptBoundary
|
|
191
172
|
|
|
192
173
|
|
|
193
174
|
class TimeIndexer:
|
|
194
175
|
"""The time indexer indexes by default from >= to the time start, and strictly < to time stop"""
|
|
195
176
|
|
|
196
|
-
|
|
197
|
-
|
|
177
|
+
_start_operation: OperatorType
|
|
178
|
+
_stop_operation: OperatorType
|
|
198
179
|
|
|
199
|
-
|
|
200
|
-
_stop_operation = Boundary.get_operation("stop", stop_mode)
|
|
201
|
-
|
|
202
|
-
def __init__(self, array: TimeCompatibleProtocol):
|
|
180
|
+
def __init__(self, array: TimeCompatibleProtocol, start="inclusive", stop="exclusive"):
|
|
203
181
|
self.array = array
|
|
182
|
+
self.set_edge_policy(start, stop)
|
|
183
|
+
|
|
184
|
+
def set_edge_policy(self, start="inclusive", stop="exclusive"):
|
|
185
|
+
self._start_operation = EdgePolicy["start"].value[start]
|
|
186
|
+
self._stop_operation = EdgePolicy["stop"].value[stop]
|
|
187
|
+
return self
|
|
204
188
|
|
|
205
189
|
def time_to_index(
|
|
206
190
|
self, time: float | int | slice | Tuple[int | float] | List[float | int | slice | Tuple[int | float]]
|
|
@@ -231,7 +215,7 @@ class TimeIndexer:
|
|
|
231
215
|
elif isinstance(time, (int, float)):
|
|
232
216
|
return self.get_iindex(sec_start=time).start
|
|
233
217
|
else:
|
|
234
|
-
raise ValueError("Cannot process time to index
|
|
218
|
+
raise ValueError("Cannot process time to index")
|
|
235
219
|
|
|
236
220
|
seconds_to_index = time_to_index
|
|
237
221
|
|
|
@@ -335,17 +319,16 @@ class TimeIndexer:
|
|
|
335
319
|
step = 1
|
|
336
320
|
return slice(start, stop, step)
|
|
337
321
|
|
|
338
|
-
def __call__(self, start=
|
|
339
|
-
|
|
340
|
-
self._start_operation = Boundary.get_operation("start", start)
|
|
341
|
-
if stop is not None:
|
|
342
|
-
self._stop_operation = Boundary.get_operation("stop", stop)
|
|
322
|
+
def __call__(self, start="inclusive", stop="exclusive"):
|
|
323
|
+
return self.set_edge_policy(start, stop)
|
|
343
324
|
|
|
344
325
|
|
|
345
326
|
class TimeMixin:
|
|
346
327
|
|
|
347
328
|
time_dimension: int
|
|
348
329
|
timeline: Timeline
|
|
330
|
+
start_policy = "inclusive"
|
|
331
|
+
stop_policy = "exclusive"
|
|
349
332
|
|
|
350
333
|
def _time_dimension_in_axis(self, axis: int | Tuple[int, ...] | None) -> bool:
|
|
351
334
|
"""Check if the time dimension is present in the specified axis.
|
|
@@ -502,7 +485,7 @@ class TimeMixin:
|
|
|
502
485
|
|
|
503
486
|
return index, final_timeline, final_time_dimension
|
|
504
487
|
|
|
505
|
-
def _get_indexed_times(self, index: int | Tuple[int, ...] | slice | Tuple[slice] | List | np.ndarray):
|
|
488
|
+
def _get_indexed_times(self, index: int | Tuple[int, ...] | slice | Tuple[slice, ...] | List | np.ndarray):
|
|
506
489
|
"""Get indexed times based on the provided index.
|
|
507
490
|
|
|
508
491
|
Args:
|
|
@@ -531,7 +514,9 @@ class TimeMixin:
|
|
|
531
514
|
|
|
532
515
|
return obj.shape == ()
|
|
533
516
|
|
|
534
|
-
def _finish_axis_removing_operation(
|
|
517
|
+
def _finish_axis_removing_operation(
|
|
518
|
+
self, result: "TimelinedArray| MaskedTimelinedArray | np.ndarray | int | float", axis: int | Tuple[int, ...]
|
|
519
|
+
) -> "TimelinedArray| MaskedTimelinedArray | np.ndarray | int | float":
|
|
535
520
|
"""Finish axis removing operation.
|
|
536
521
|
|
|
537
522
|
Args:
|
|
@@ -622,7 +607,7 @@ class TimeMixin:
|
|
|
622
607
|
def itime(self: TimeCompatibleProtocol):
|
|
623
608
|
"""Return a TimeIndexer object based on the given TimeCompatibleProtocol object."""
|
|
624
609
|
|
|
625
|
-
return TimeIndexer(self)
|
|
610
|
+
return TimeIndexer(self, self.start_policy, self.stop_policy)
|
|
626
611
|
|
|
627
612
|
isec = itime
|
|
628
613
|
|
|
@@ -673,7 +658,11 @@ class TimeMixin:
|
|
|
673
658
|
|
|
674
659
|
shift_area = self.itime.__getitem__(period) if time_period else self.__getitem__(period)
|
|
675
660
|
|
|
676
|
-
|
|
661
|
+
# if not this : we lost a dimension because we sliced one axis to a single element, no need to no .mean
|
|
662
|
+
if not len(shift_area.shape) < len(self.shape):
|
|
663
|
+
shift_area = shift_area.mean(axis=axis)
|
|
664
|
+
|
|
665
|
+
return self - np.repeat(shift_area.__getitem__(tuple(indexer)), self.shape[axis], axis=axis)
|
|
677
666
|
|
|
678
667
|
def swapaxes(self: TimeCompatibleProtocol, axis1: int, axis2: int):
|
|
679
668
|
"""Swap the two specified axes of the TimelinedArray.
|
|
@@ -1180,7 +1169,7 @@ class TimelinedArray(TimeMixin, np.ndarray, TimeCompatibleProtocol):
|
|
|
1180
1169
|
index, final_timeline, final_time_dimension = self._get_indexed_times(index)
|
|
1181
1170
|
|
|
1182
1171
|
if final_timeline is None or final_time_dimension is None:
|
|
1183
|
-
return np.
|
|
1172
|
+
return np.asarray(self).__getitem__(index)
|
|
1184
1173
|
|
|
1185
1174
|
indexed_result = super().__getitem__(index)
|
|
1186
1175
|
|
timelined_array-0.0.7/PKG-INFO
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: timelined_array
|
|
3
|
-
Version: 0.0.7
|
|
4
|
-
Summary: Manage easily 1 or multidimensionnal samples numpy arrays that are time related. Extends numpy without removing any of it's abilities on such arrays.
|
|
5
|
-
Author-Email: Timothe Jost <timothe.jost@wanadoo.fr>
|
|
6
|
-
License: MIT
|
|
7
|
-
Requires-Python: >=3.11
|
|
8
|
-
Requires-Dist: numpy
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
|
|
11
|
-
# timelined_array
|
|
12
|
-
|
|
13
|
-
## Overview
|
|
14
|
-
The TimelinedArray package provides a set of classes and utilities for working with time-indexed arrays. It extends the functionality of NumPy arrays to include time-based indexing and operations, making it easier to work with time-series data.
|
|
15
|
-
|
|
16
|
-
## Classes
|
|
17
|
-
### Timeline
|
|
18
|
-
A subclass of np.ndarray that represents a timeline associated with the array. It includes methods for creating uniformly spaced timelines and calculating time steps.
|
|
19
|
-
|
|
20
|
-
### Boundary
|
|
21
|
-
An enumeration that defines inclusive and exclusive boundaries for time indexing.
|
|
22
|
-
|
|
23
|
-
### TimeIndexer
|
|
24
|
-
A class that provides methods for converting time values to array indices and for indexing arrays based on time.
|
|
25
|
-
|
|
26
|
-
### TimeMixin
|
|
27
|
-
A mixin class that adds time-related methods and properties to arrays, including methods for aligning, transposing, and moving axes.
|
|
28
|
-
|
|
29
|
-
### TimePacker
|
|
30
|
-
A class for packing arrays with their associated timelines. Usefull to plot data fast to matplotlib.
|
|
31
|
-
|
|
32
|
-
### TimelinedArray
|
|
33
|
-
A subclass of np.ndarray that includes a timeline and a time dimension. It provides methods for time-based indexing and operations.
|
|
34
|
-
|
|
35
|
-
### MaskedTimelinedArray
|
|
36
|
-
A subclass of np.ma.MaskedArray that includes a timeline and a time dimension. It provides methods for time-based indexing and operations on masked arrays.
|
|
37
|
-
|
|
38
|
-
### Seconds
|
|
39
|
-
A simple class for converting seconds to array indices based on a given sampling frequency.
|
|
40
|
-
|
|
41
|
-
## Installation
|
|
42
|
-
To install the TimelinedArray package, simply type in your environment activated console :
|
|
43
|
-
```bash
|
|
44
|
-
pip install timelined_array
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
The package can be found on PyPI at : https://pypi.org/project/timelined_array/
|
|
48
|
-
|
|
49
|
-
## Usage
|
|
50
|
-
|
|
51
|
-
### Imports
|
|
52
|
-
```python
|
|
53
|
-
from timelined_array import TimelinedArray, MaskedTimelinedArray, Boundary, Timeline
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### Creating a TimelinedArray
|
|
57
|
-
```python
|
|
58
|
-
import numpy as np
|
|
59
|
-
from timelined_array import TimelinedArray
|
|
60
|
-
|
|
61
|
-
data = np.random.rand(100, 10)
|
|
62
|
-
timeline = np.linspace(0, 10, 100)
|
|
63
|
-
timelined_array = TimelinedArray(data, timeline=timeline, time_dimension=0)
|
|
64
|
-
```
|
|
65
|
-
### Time-based Indexing
|
|
66
|
-
```python
|
|
67
|
-
# Access data at a specific time
|
|
68
|
-
data_at_time = timelined_array.itime[5.0]
|
|
69
|
-
|
|
70
|
-
# Aligning arrays
|
|
71
|
-
aligned_array = TimelinedArray.align_from_iterable([timelined_array, another_timelined_array])
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
### Masked TimelinedArray
|
|
76
|
-
```python
|
|
77
|
-
from timelined_array import MaskedTimelinedArray
|
|
78
|
-
|
|
79
|
-
masked_data = np.ma.masked_array(data, mask=data > 0.5)
|
|
80
|
-
masked_timelined_array = MaskedTimelinedArray(masked_data, timeline=timeline, time_dimension=0)
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### Converting Seconds to Index
|
|
84
|
-
```python
|
|
85
|
-
masked_data.itime.time_to_index(5.0)
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## Misc
|
|
89
|
-
|
|
90
|
-
### License
|
|
91
|
-
This package is licensed under the MIT License. See the LICENSE file for more details.
|
|
92
|
-
|
|
93
|
-
### Contributing
|
|
94
|
-
Contributions are welcome! Please submit a pull request or open an issue to discuss any changes.
|
|
95
|
-
|
|
96
|
-
### Contact
|
|
97
|
-
For any questions or issues, please contact the package maintainer.
|
timelined_array-0.0.7/README.md
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
# timelined_array
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
The TimelinedArray package provides a set of classes and utilities for working with time-indexed arrays. It extends the functionality of NumPy arrays to include time-based indexing and operations, making it easier to work with time-series data.
|
|
5
|
-
|
|
6
|
-
## Classes
|
|
7
|
-
### Timeline
|
|
8
|
-
A subclass of np.ndarray that represents a timeline associated with the array. It includes methods for creating uniformly spaced timelines and calculating time steps.
|
|
9
|
-
|
|
10
|
-
### Boundary
|
|
11
|
-
An enumeration that defines inclusive and exclusive boundaries for time indexing.
|
|
12
|
-
|
|
13
|
-
### TimeIndexer
|
|
14
|
-
A class that provides methods for converting time values to array indices and for indexing arrays based on time.
|
|
15
|
-
|
|
16
|
-
### TimeMixin
|
|
17
|
-
A mixin class that adds time-related methods and properties to arrays, including methods for aligning, transposing, and moving axes.
|
|
18
|
-
|
|
19
|
-
### TimePacker
|
|
20
|
-
A class for packing arrays with their associated timelines. Usefull to plot data fast to matplotlib.
|
|
21
|
-
|
|
22
|
-
### TimelinedArray
|
|
23
|
-
A subclass of np.ndarray that includes a timeline and a time dimension. It provides methods for time-based indexing and operations.
|
|
24
|
-
|
|
25
|
-
### MaskedTimelinedArray
|
|
26
|
-
A subclass of np.ma.MaskedArray that includes a timeline and a time dimension. It provides methods for time-based indexing and operations on masked arrays.
|
|
27
|
-
|
|
28
|
-
### Seconds
|
|
29
|
-
A simple class for converting seconds to array indices based on a given sampling frequency.
|
|
30
|
-
|
|
31
|
-
## Installation
|
|
32
|
-
To install the TimelinedArray package, simply type in your environment activated console :
|
|
33
|
-
```bash
|
|
34
|
-
pip install timelined_array
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
The package can be found on PyPI at : https://pypi.org/project/timelined_array/
|
|
38
|
-
|
|
39
|
-
## Usage
|
|
40
|
-
|
|
41
|
-
### Imports
|
|
42
|
-
```python
|
|
43
|
-
from timelined_array import TimelinedArray, MaskedTimelinedArray, Boundary, Timeline
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### Creating a TimelinedArray
|
|
47
|
-
```python
|
|
48
|
-
import numpy as np
|
|
49
|
-
from timelined_array import TimelinedArray
|
|
50
|
-
|
|
51
|
-
data = np.random.rand(100, 10)
|
|
52
|
-
timeline = np.linspace(0, 10, 100)
|
|
53
|
-
timelined_array = TimelinedArray(data, timeline=timeline, time_dimension=0)
|
|
54
|
-
```
|
|
55
|
-
### Time-based Indexing
|
|
56
|
-
```python
|
|
57
|
-
# Access data at a specific time
|
|
58
|
-
data_at_time = timelined_array.itime[5.0]
|
|
59
|
-
|
|
60
|
-
# Aligning arrays
|
|
61
|
-
aligned_array = TimelinedArray.align_from_iterable([timelined_array, another_timelined_array])
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
### Masked TimelinedArray
|
|
66
|
-
```python
|
|
67
|
-
from timelined_array import MaskedTimelinedArray
|
|
68
|
-
|
|
69
|
-
masked_data = np.ma.masked_array(data, mask=data > 0.5)
|
|
70
|
-
masked_timelined_array = MaskedTimelinedArray(masked_data, timeline=timeline, time_dimension=0)
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Converting Seconds to Index
|
|
74
|
-
```python
|
|
75
|
-
masked_data.itime.time_to_index(5.0)
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## Misc
|
|
79
|
-
|
|
80
|
-
### License
|
|
81
|
-
This package is licensed under the MIT License. See the LICENSE file for more details.
|
|
82
|
-
|
|
83
|
-
### Contributing
|
|
84
|
-
Contributions are welcome! Please submit a pull request or open an issue to discuss any changes.
|
|
85
|
-
|
|
86
|
-
### Contact
|
|
87
|
-
For any questions or issues, please contact the package maintainer.
|
|
File without changes
|
|
File without changes
|