pfc-geometry 0.2.9__py3-none-any.whl → 0.2.10__py3-none-any.whl
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.
- geometry/base.py +102 -81
- {pfc_geometry-0.2.9.dist-info → pfc_geometry-0.2.10.dist-info}/METADATA +1 -1
- {pfc_geometry-0.2.9.dist-info → pfc_geometry-0.2.10.dist-info}/RECORD +6 -6
- {pfc_geometry-0.2.9.dist-info → pfc_geometry-0.2.10.dist-info}/WHEEL +1 -1
- {pfc_geometry-0.2.9.dist-info → pfc_geometry-0.2.10.dist-info}/LICENSE +0 -0
- {pfc_geometry-0.2.9.dist-info → pfc_geometry-0.2.10.dist-info}/top_level.txt +0 -0
geometry/base.py
CHANGED
|
@@ -18,26 +18,31 @@ from numbers import Number
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def dprep(func):
|
|
21
|
-
"""this decorates a method that works on numpy arrays of shape equal to self.data.
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
"""this decorates a method that works on numpy arrays of shape equal to self.data.
|
|
22
|
+
you can pass a nupy array or an instance of self.__class__. As long as the length
|
|
23
|
+
is the same as self, 1, or len(self) == 1 it should construct the arguments for the decorated function.
|
|
24
24
|
"""
|
|
25
|
+
|
|
25
26
|
def wrapper(self, b):
|
|
26
27
|
bdat = self._dprep(b)
|
|
27
|
-
|
|
28
|
+
|
|
28
29
|
if len(bdat) > 1 and len(self) == 1:
|
|
29
30
|
a = self.tile(len(bdat))
|
|
30
31
|
else:
|
|
31
32
|
a = self
|
|
32
33
|
return func(a, bdat)
|
|
33
|
-
|
|
34
|
+
|
|
34
35
|
return wrapper
|
|
35
36
|
|
|
37
|
+
|
|
36
38
|
class Base:
|
|
37
|
-
__array_priority__ =
|
|
38
|
-
|
|
39
|
+
__array_priority__ = (
|
|
40
|
+
15.0 # this is a quirk of numpy so the __r*__ methods here take priority
|
|
41
|
+
)
|
|
42
|
+
cols = []
|
|
39
43
|
from_np_base = []
|
|
40
44
|
from_np = []
|
|
45
|
+
|
|
41
46
|
def __init__(self, *args, **kwargs):
|
|
42
47
|
if len(kwargs) > 0:
|
|
43
48
|
if len(args) > 0:
|
|
@@ -47,23 +52,31 @@ class Base:
|
|
|
47
52
|
elif "data" in kwargs:
|
|
48
53
|
args = [kwargs["data"]]
|
|
49
54
|
else:
|
|
50
|
-
raise TypeError(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
raise TypeError(
|
|
56
|
+
f"unknown kwargs passed to {self.__class__.__name__}: {args}"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if len(args) == 1:
|
|
60
|
+
if isinstance(args[0], np.ndarray) or isinstance(
|
|
61
|
+
args[0], list
|
|
62
|
+
): # data was passed directly
|
|
54
63
|
self.data = self.__class__._clean_data(np.array(args[0]))
|
|
55
64
|
|
|
56
65
|
elif all([isinstance(a, self.__class__) for a in args[0]]):
|
|
57
|
-
#a list of self.__class__ is passed, concatenate into one
|
|
58
|
-
self.data = self.__class__._clean_data(
|
|
59
|
-
|
|
66
|
+
# a list of self.__class__ is passed, concatenate into one
|
|
67
|
+
self.data = self.__class__._clean_data(
|
|
68
|
+
np.concatenate([a.data for a in args[0]])
|
|
69
|
+
)
|
|
70
|
+
|
|
60
71
|
elif isinstance(args[0], pd.DataFrame):
|
|
61
72
|
self.data = self.__class__._clean_data(np.array(args[0]))
|
|
62
73
|
else:
|
|
63
|
-
raise TypeError(
|
|
64
|
-
|
|
74
|
+
raise TypeError(
|
|
75
|
+
f"unknown args passed to {self.__class__.__name__}: {args[0]}"
|
|
76
|
+
)
|
|
77
|
+
|
|
65
78
|
elif len(args) == len(self.__class__.cols):
|
|
66
|
-
#three args passed, each represents a col
|
|
79
|
+
# three args passed, each represents a col
|
|
67
80
|
if all(isinstance(arg, Number) for arg in args):
|
|
68
81
|
self.data = self.__class__._clean_data(np.array(args))
|
|
69
82
|
elif all(isinstance(arg, np.ndarray) for arg in args):
|
|
@@ -71,7 +84,9 @@ class Base:
|
|
|
71
84
|
elif all(isinstance(arg, list) for arg in args):
|
|
72
85
|
self.data = self.__class__._clean_data(np.array(args).T)
|
|
73
86
|
elif all(isinstance(arg, pd.Series) for arg in args):
|
|
74
|
-
self.data = self.__class__._clean_data(
|
|
87
|
+
self.data = self.__class__._clean_data(
|
|
88
|
+
np.array(pd.concat(args, axis=1))
|
|
89
|
+
)
|
|
75
90
|
else:
|
|
76
91
|
raise TypeError
|
|
77
92
|
else:
|
|
@@ -80,16 +95,18 @@ class Base:
|
|
|
80
95
|
@classmethod
|
|
81
96
|
def _clean_data(cls, data) -> npt.NDArray[np.float64]:
|
|
82
97
|
assert isinstance(data, np.ndarray)
|
|
83
|
-
if data.dtype ==
|
|
84
|
-
raise TypeError(
|
|
98
|
+
if data.dtype == "O":
|
|
99
|
+
raise TypeError(
|
|
100
|
+
f"data must have homogeneous shape for {cls.__name__}, given {data.shape}"
|
|
101
|
+
)
|
|
85
102
|
if len(data.shape) == 1:
|
|
86
103
|
data = data.reshape(1, len(data))
|
|
87
|
-
|
|
104
|
+
|
|
88
105
|
assert data.shape[1] == len(cls.cols)
|
|
89
106
|
return data
|
|
90
107
|
|
|
91
108
|
@classmethod
|
|
92
|
-
def type_check(cls,a):
|
|
109
|
+
def type_check(cls, a):
|
|
93
110
|
return a if isinstance(a, cls) else cls(a)
|
|
94
111
|
|
|
95
112
|
@classmethod
|
|
@@ -99,7 +116,9 @@ class Base:
|
|
|
99
116
|
elif len(b) == 1 and len(a) > 1:
|
|
100
117
|
b = b.tile(len(a))
|
|
101
118
|
elif len(a) > 1 and len(b) > 1 and not len(a) == len(b):
|
|
102
|
-
raise TypeError(
|
|
119
|
+
raise TypeError(
|
|
120
|
+
f"lengths of passed arguments must be equal or 1, got {len(a)}, {len(b)}"
|
|
121
|
+
)
|
|
103
122
|
return a, b
|
|
104
123
|
|
|
105
124
|
@classmethod
|
|
@@ -108,16 +127,16 @@ class Base:
|
|
|
108
127
|
|
|
109
128
|
def __getattr__(self, name) -> npt.NDArray[np.float64]:
|
|
110
129
|
if name in self.__class__.cols:
|
|
111
|
-
return self.data[:,self.__class__.cols.index(name)]
|
|
112
|
-
#return res[0] if len(res) == 1 else res
|
|
130
|
+
return self.data[:, self.__class__.cols.index(name)]
|
|
131
|
+
# return res[0] if len(res) == 1 else res
|
|
113
132
|
elif name in self.__class__.from_np + self.__class__.from_np_base:
|
|
114
133
|
return self.__class__(getattr(np, name)(self.data))
|
|
115
134
|
else:
|
|
116
135
|
for col in self.__class__.cols:
|
|
117
136
|
if len(name) > len(col):
|
|
118
|
-
if name[:len(col)] == col:
|
|
137
|
+
if name[: len(col)] == col:
|
|
119
138
|
try:
|
|
120
|
-
id = int(name[len(col):])
|
|
139
|
+
id = int(name[len(col) :])
|
|
121
140
|
except ValueError:
|
|
122
141
|
break
|
|
123
142
|
return getattr(self, col)[id]
|
|
@@ -128,31 +147,31 @@ class Base:
|
|
|
128
147
|
return self.__class__.cols
|
|
129
148
|
|
|
130
149
|
def __getitem__(self, sli) -> Self:
|
|
131
|
-
return self.__class__(self.data[sli
|
|
150
|
+
return self.__class__(self.data[sli, :])
|
|
132
151
|
|
|
133
|
-
def _dprep(self, other):
|
|
134
|
-
l
|
|
152
|
+
def _dprep(self, other):
|
|
153
|
+
l, w = len(self), len(self.cols)
|
|
135
154
|
|
|
136
155
|
if isinstance(other, np.ndarray):
|
|
137
|
-
if other.shape == (l,w):
|
|
156
|
+
if other.shape == (l, w):
|
|
138
157
|
return other
|
|
139
158
|
elif other.shape == (l, 1) or other.shape == (l,):
|
|
140
|
-
return np.tile(other, (w,1)).T
|
|
159
|
+
return np.tile(other, (w, 1)).T
|
|
141
160
|
elif other.shape == (1,):
|
|
142
|
-
return np.full((l,w), other[0])
|
|
143
|
-
elif l==1:
|
|
161
|
+
return np.full((l, w), other[0])
|
|
162
|
+
elif l == 1:
|
|
144
163
|
if len(other.shape) == 1:
|
|
145
|
-
return np.tile(other, (w,1)).T
|
|
164
|
+
return np.tile(other, (w, 1)).T
|
|
146
165
|
elif other.shape[1] == w:
|
|
147
166
|
return other
|
|
148
167
|
else:
|
|
149
|
-
raise ValueError(f"array shape {other.shape} not handled")
|
|
168
|
+
raise ValueError(f"array shape {other.shape} not handled")
|
|
150
169
|
else:
|
|
151
170
|
raise ValueError(f"array shape {other.shape} not handled")
|
|
152
171
|
elif isinstance(other, float) or isinstance(other, int):
|
|
153
|
-
return np.full((l,w), other)
|
|
172
|
+
return np.full((l, w), other)
|
|
154
173
|
elif isinstance(other, Base):
|
|
155
|
-
a,b = self.__class__.length_check(self, other)
|
|
174
|
+
a, b = self.__class__.length_check(self, other)
|
|
156
175
|
return self._dprep(b.data)
|
|
157
176
|
else:
|
|
158
177
|
raise ValueError(f"unhandled datatype ({other.__class__.name})")
|
|
@@ -163,7 +182,7 @@ class Base:
|
|
|
163
182
|
def degrees(self) -> Self:
|
|
164
183
|
return self.__class__(np.degrees(self.data))
|
|
165
184
|
|
|
166
|
-
def count(self) ->
|
|
185
|
+
def count(self) -> int:
|
|
167
186
|
return len(self)
|
|
168
187
|
|
|
169
188
|
def __len__(self) -> int:
|
|
@@ -171,7 +190,7 @@ class Base:
|
|
|
171
190
|
|
|
172
191
|
@property
|
|
173
192
|
def ends(self) -> Self:
|
|
174
|
-
return self.__class__(self.data[[0
|
|
193
|
+
return self.__class__(self.data[[0, -1], :])
|
|
175
194
|
|
|
176
195
|
@dprep
|
|
177
196
|
def __eq__(self, other):
|
|
@@ -180,7 +199,7 @@ class Base:
|
|
|
180
199
|
@dprep
|
|
181
200
|
def __add__(self, other) -> Self:
|
|
182
201
|
return self.__class__(self.data + other)
|
|
183
|
-
|
|
202
|
+
|
|
184
203
|
@dprep
|
|
185
204
|
def __radd__(self, other) -> Self:
|
|
186
205
|
return self.__class__(other + self.data)
|
|
@@ -188,7 +207,7 @@ class Base:
|
|
|
188
207
|
@dprep
|
|
189
208
|
def __sub__(self, other) -> Self:
|
|
190
209
|
return self.__class__(self.data - other)
|
|
191
|
-
|
|
210
|
+
|
|
192
211
|
@dprep
|
|
193
212
|
def __rsub__(self, other) -> Self:
|
|
194
213
|
return self.__class__(other - self.data)
|
|
@@ -210,12 +229,14 @@ class Base:
|
|
|
210
229
|
return self.__class__(self.data / other)
|
|
211
230
|
|
|
212
231
|
def __str__(self):
|
|
213
|
-
means =
|
|
214
|
-
|
|
232
|
+
means = " ".join(
|
|
233
|
+
f"{c}_={v}" for c, v in zip(self.cols, np.mean(self.data, axis=0).round(2))
|
|
234
|
+
)
|
|
235
|
+
return f"{self.__class__.__name__}({means}, len={len(self)})"
|
|
215
236
|
|
|
216
237
|
def __abs__(self):
|
|
217
238
|
return np.linalg.norm(self.data, axis=1)
|
|
218
|
-
|
|
239
|
+
|
|
219
240
|
def abs(self) -> Self:
|
|
220
241
|
return self.__class__(np.abs(self.data))
|
|
221
242
|
|
|
@@ -224,27 +245,23 @@ class Base:
|
|
|
224
245
|
|
|
225
246
|
@dprep
|
|
226
247
|
def dot(self, other: Self) -> Self:
|
|
227
|
-
return np.einsum(
|
|
248
|
+
return np.einsum("ij,ij->i", self.data, other)
|
|
228
249
|
|
|
229
|
-
def diff(self, dt:np.array) -> Self:
|
|
250
|
+
def diff(self, dt: np.array) -> Self:
|
|
230
251
|
if not pd.api.types.is_list_like(dt):
|
|
231
252
|
dt = np.full(len(self), dt)
|
|
232
253
|
assert len(dt) == len(self)
|
|
233
254
|
return self.__class__(
|
|
234
|
-
np.gradient(self.data,axis=0)
|
|
235
|
-
|
|
236
|
-
|
|
255
|
+
np.gradient(self.data, axis=0)
|
|
256
|
+
/ np.tile(dt, (len(self.__class__.cols), 1)).T
|
|
257
|
+
)
|
|
237
258
|
|
|
238
|
-
def to_pandas(self, prefix=
|
|
259
|
+
def to_pandas(self, prefix="", suffix="", columns=None, index=None):
|
|
239
260
|
if columns is not None:
|
|
240
261
|
cols = columns
|
|
241
262
|
else:
|
|
242
263
|
cols = [prefix + col + suffix for col in self.__class__.cols]
|
|
243
|
-
return pd.DataFrame(
|
|
244
|
-
self.data,
|
|
245
|
-
columns=cols,
|
|
246
|
-
index=index
|
|
247
|
-
)
|
|
264
|
+
return pd.DataFrame(self.data, columns=cols, index=index)
|
|
248
265
|
|
|
249
266
|
def tile(self, count) -> Self:
|
|
250
267
|
return self.__class__(np.tile(self.data, (count, 1)))
|
|
@@ -254,14 +271,14 @@ class Base:
|
|
|
254
271
|
return {key: getattr(self, key)[0] for key in self.cols}
|
|
255
272
|
else:
|
|
256
273
|
return {key: getattr(self, key) for key in self.cols}
|
|
257
|
-
|
|
274
|
+
|
|
258
275
|
@classmethod
|
|
259
276
|
def from_dict(Cls, data):
|
|
260
277
|
return Cls(**data)
|
|
261
278
|
|
|
262
279
|
def to_dicts(self):
|
|
263
|
-
return self.to_pandas().to_dict(
|
|
264
|
-
|
|
280
|
+
return self.to_pandas().to_dict("records")
|
|
281
|
+
|
|
265
282
|
@classmethod
|
|
266
283
|
def from_dicts(Cls, data: dict):
|
|
267
284
|
return Cls(pd.DataFrame.from_dict(data))
|
|
@@ -282,53 +299,57 @@ class Base:
|
|
|
282
299
|
def maxloc(self):
|
|
283
300
|
return self.__class__(self.data.argmax(axis=0))
|
|
284
301
|
|
|
285
|
-
|
|
286
302
|
def cumsum(self):
|
|
287
|
-
return self.__class__(np.cumsum(self.data,axis=0))
|
|
303
|
+
return self.__class__(np.cumsum(self.data, axis=0))
|
|
288
304
|
|
|
289
305
|
def round(self, decimals=0):
|
|
290
306
|
return self.__class__(self.data.round(decimals))
|
|
291
|
-
|
|
307
|
+
|
|
292
308
|
def __repr__(self):
|
|
293
309
|
return str(self)
|
|
294
|
-
|
|
310
|
+
|
|
295
311
|
def copy(self):
|
|
296
312
|
return self.__class__(self.data.copy())
|
|
297
|
-
|
|
298
313
|
|
|
299
314
|
def unwrap(self, discont=np.pi):
|
|
300
315
|
return self.__class__(np.unwrap(self.data, discont=discont, axis=0))
|
|
301
|
-
|
|
302
|
-
def filter(self, order, cutoff, ts: np.ndarray=None):
|
|
316
|
+
|
|
317
|
+
def filter(self, order, cutoff, ts: np.ndarray = None):
|
|
303
318
|
from scipy.signal import butter, freqz, filtfilt
|
|
319
|
+
|
|
304
320
|
if ts is None:
|
|
305
321
|
ts = np.array(range(len(self)))
|
|
306
322
|
N = len(self)
|
|
307
323
|
T = (ts[-1] - ts[0]) / N
|
|
308
324
|
|
|
309
|
-
fs = 1/T
|
|
310
|
-
b, a = butter(
|
|
311
|
-
order,
|
|
312
|
-
cutoff,
|
|
313
|
-
fs=fs,
|
|
314
|
-
btype='low', analog=False
|
|
315
|
-
)
|
|
325
|
+
fs = 1 / T
|
|
326
|
+
b, a = butter(order, cutoff, fs=fs, btype="low", analog=False)
|
|
316
327
|
|
|
317
328
|
return self.__class__(filtfilt(b, a, self.data, axis=0))
|
|
318
|
-
|
|
319
|
-
def fft(self, ts: np.ndarray=None):
|
|
329
|
+
|
|
330
|
+
def fft(self, ts: np.ndarray = None):
|
|
320
331
|
from scipy.fft import fft, fftfreq
|
|
332
|
+
|
|
321
333
|
if ts is None:
|
|
322
334
|
ts = np.array(range(len(self)))
|
|
323
|
-
N = len(self)*2
|
|
335
|
+
N = len(self) * 2
|
|
324
336
|
T = (ts[-1] - ts[0]) / len(self)
|
|
325
337
|
|
|
326
338
|
yf = fft(self.data, axis=0, n=N)
|
|
327
|
-
xf = fftfreq(N, T)[:N//2]
|
|
339
|
+
xf = fftfreq(N, T)[: N // 2]
|
|
328
340
|
|
|
329
|
-
|
|
330
|
-
y=2.0/N * np.abs(yf[0:N//2, :])
|
|
341
|
+
y = 2.0 / N * np.abs(yf[0 : N // 2, :])
|
|
331
342
|
|
|
332
|
-
return pd.DataFrame(
|
|
343
|
+
return pd.DataFrame(
|
|
344
|
+
np.column_stack([xf, y]), columns=["freq"] + self.cols
|
|
345
|
+
).set_index("freq")
|
|
333
346
|
|
|
334
|
-
|
|
347
|
+
def fill_zeros(self):
|
|
348
|
+
"""fills zero length rows with the previous or next non-zero value"""
|
|
349
|
+
return self.__class__(
|
|
350
|
+
pd.DataFrame(np.where(
|
|
351
|
+
np.tile(abs(self) == 0, (3, 1)).T,
|
|
352
|
+
np.full(self.data.shape, np.nan),
|
|
353
|
+
self.data,
|
|
354
|
+
)).ffill().bfill().to_numpy()
|
|
355
|
+
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
geometry/__init__.py,sha256=HNhMyemIJzDq1nDjrr09eX5PS7q9ULscSbYsXss3JRM,1253
|
|
2
|
-
geometry/base.py,sha256=
|
|
2
|
+
geometry/base.py,sha256=rJSDViQlW6zHwP6n2VT7uulfargKIDEmUCV5sPhghOM,11735
|
|
3
3
|
geometry/coordinate_frame.py,sha256=qO3jqUWF4PW4BOkw5rBMTXfOzBjlA1HYwGYmPtu2M8M,3039
|
|
4
4
|
geometry/gps.py,sha256=Fs3hakSQ754HUqRsA7NWg_MSEdYxNqyiu4gu6EDrFqI,3381
|
|
5
5
|
geometry/mass.py,sha256=BUWBSITwpdRfpJR5-oJTd16BI7FLZt8rhxdzr0cx1HY,1675
|
|
@@ -8,8 +8,8 @@ geometry/quaternion.py,sha256=PH7YKAVnQ8vZ8RZdtnrIaqx3FmHHwQ2aTEEdofrng_U,9392
|
|
|
8
8
|
geometry/testing.py,sha256=o8yMBAdU5Vy0EspBYaof4fPGgRSFZhRDhzBjRPsLd0M,375
|
|
9
9
|
geometry/time.py,sha256=ZWO6yThXesHkNsROJfUruF-G4Lpcx149x0H__0HPLvw,1206
|
|
10
10
|
geometry/transformation.py,sha256=eunEC924zPBLhaVSEWE-IClrRfitvUKjSdOTaa2tdDQ,4705
|
|
11
|
-
pfc_geometry-0.2.
|
|
12
|
-
pfc_geometry-0.2.
|
|
13
|
-
pfc_geometry-0.2.
|
|
14
|
-
pfc_geometry-0.2.
|
|
15
|
-
pfc_geometry-0.2.
|
|
11
|
+
pfc_geometry-0.2.10.dist-info/LICENSE,sha256=z72U6pv-bQgJ_Svr4uCXnMjemsp38aSerhHEdEAOMJ4,7632
|
|
12
|
+
pfc_geometry-0.2.10.dist-info/METADATA,sha256=aIrRdNxRV07oKLdlKXzsjcano6Y_q36vR2ONr-XRrew,1979
|
|
13
|
+
pfc_geometry-0.2.10.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
|
|
14
|
+
pfc_geometry-0.2.10.dist-info/top_level.txt,sha256=RWbWhWYclEM-OFtXLCB4eLv-jxNnlJB-NPC2YM587Fo,9
|
|
15
|
+
pfc_geometry-0.2.10.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|