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 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
- 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.
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__ = 15.0 # this is a quirk of numpy so the __r*__ methods here take priority
38
- cols=[]
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(f"unknown kwargs passed to {self.__class__.__name__}: {args}")
51
-
52
- if len(args)==1:
53
- if isinstance(args[0], np.ndarray) or isinstance(args[0], list): #data was passed directly
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(np.concatenate([a.data for a in args[0]]))
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(f"unknown args passed to {self.__class__.__name__}: {args[0]}")
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(np.array(pd.concat(args, axis=1)))
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 == 'O':
84
- raise TypeError(f'data must have homogeneous shape for {cls.__name__}, given {data.shape}')
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(f"lengths of passed arguments must be equal or 1, got {len(a)}, {len(b)}")
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 , w = len(self), len(self.cols)
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) -> int:
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,-1], :])
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 = ' '.join(f'{c}_={v}' for c, v in zip(self.cols, np.mean(self.data, axis=0).round(2)))
214
- return f'{self.__class__.__name__}({means}, len={len(self)})'
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('ij,ij->i', self.data, other)
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
- np.tile(dt, (len(self.__class__.cols),1)).T)
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='', suffix='', columns=None, index=None):
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('records')
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(np.column_stack([xf, y]), columns=['freq'] + self.cols).set_index('freq')
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pfc-geometry
3
- Version: 0.2.9
3
+ Version: 0.2.10
4
4
  Summary: A package for handling 3D geometry with a nice interface
5
5
  Author-email: Thomas David <thomasdavid0@gmail.com>
6
6
  License: GNU GPL v3
@@ -1,5 +1,5 @@
1
1
  geometry/__init__.py,sha256=HNhMyemIJzDq1nDjrr09eX5PS7q9ULscSbYsXss3JRM,1253
2
- geometry/base.py,sha256=jaufGQb6fxGAUWyKMYnilKbfaJtd6FkDKy7pBM4rv7w,11280
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.9.dist-info/LICENSE,sha256=z72U6pv-bQgJ_Svr4uCXnMjemsp38aSerhHEdEAOMJ4,7632
12
- pfc_geometry-0.2.9.dist-info/METADATA,sha256=8mWZo2S4C_o4fPrO8mZP11xd6w0na5lg7ebzZnpj8Qc,1978
13
- pfc_geometry-0.2.9.dist-info/WHEEL,sha256=nCVcAvsfA9TDtwGwhYaRrlPhTLV9m-Ga6mdyDtuwK18,91
14
- pfc_geometry-0.2.9.dist-info/top_level.txt,sha256=RWbWhWYclEM-OFtXLCB4eLv-jxNnlJB-NPC2YM587Fo,9
15
- pfc_geometry-0.2.9.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (73.0.0)
2
+ Generator: setuptools (73.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5