Skip to content

Commit d58d398

Browse files
authored
Merge pull request #211 from nasa/feature/func_tools
2 parents e6fe3e5 + 173d726 commit d58d398

File tree

1 file changed

+37
-42
lines changed

1 file changed

+37
-42
lines changed

src/progpy/sim_result.py

+37-42
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from collections import UserList, defaultdict, abc
44
from copy import deepcopy
5+
from functools import cached_property
56
from matplotlib.pyplot import figure
67
import numpy as np
78
import pandas as pd
@@ -23,7 +24,6 @@ class SimResult(UserList):
2324
__slots__ = ['times', 'data'] # Optimization
2425

2526
def __init__(self, times: list = None, data: list = None, _copy=True):
26-
self._frame = None
2727
if times is None or data is None:
2828
self.times = []
2929
self.data = []
@@ -60,27 +60,24 @@ def iterrows(self):
6060
"""
6161
return super().__iter__()
6262

63-
@property
63+
@cached_property
6464
def frame(self) -> pd.DataFrame:
6565
"""
6666
.. versionadded:: 1.5.0
6767
6868
pd.DataFrame: A pandas DataFrame representing the SimResult data
6969
"""
70-
warn_once('frame will be deprecated after version 1.5 of ProgPy.', DeprecationWarning, stacklevel=2)
71-
if self._frame is None:
72-
if len(self.data) > 0: #
73-
self._frame = pd.concat([
74-
pd.DataFrame(dict(dframe), index=[0]) for dframe in self.data
75-
], ignore_index=True, axis=0)
76-
else:
77-
self._frame = pd.DataFrame()
78-
if self.times is not None:
79-
self._frame.insert(0, "time", self.times)
80-
self._frame = self._frame.set_index('time')
81-
return self._frame
70+
# warn_once('frame will be deprecated after version 1.5 of ProgPy.', DeprecationWarning, stacklevel=2)
71+
if len(self.data) > 0:
72+
frame = pd.concat([
73+
pd.DataFrame(dict(dframe), index=[0]) for dframe in self.data
74+
], ignore_index=True, axis=0)
8275
else:
83-
return self._frame
76+
frame = pd.DataFrame()
77+
if self.times is not None:
78+
frame.insert(0, "time", self.times)
79+
frame = frame.set_index('time')
80+
return frame
8481

8582
def frame_is_empty(self) -> bool:
8683
"""
@@ -89,33 +86,33 @@ def frame_is_empty(self) -> bool:
8986
Returns:
9087
bool: If the value has been calculated
9188
"""
92-
return self._frame.empty
89+
return self.frame.empty
9390

9491
def __setitem__(self, key, value):
9592
"""
9693
in addition to the normal functionality, updates the _frame if it exists
9794
"""
9895
super().__setitem__(key, value)
99-
if self._frame is not None:
96+
if 'frame' in self.__dict__: # Has been calculated
10097
for col in value:
101-
self._frame.at[key, col] = value[col]
98+
self.__dict__['frame'].at[key, col] = value[col]
10299

103100
def __delitem__(self, key):
104101
"""
105102
in addition to the normal functionality, updates the _frame if it exists
106103
"""
107104
super().__delitem__(key)
108-
if self._frame is not None:
109-
self._frame = self._frame.drop([key])
105+
if 'frame' in self.__dict__:
106+
self.__dict__['frame'].drop([key], inplace=True)
110107

111108
def insert(self, i: int, item) -> None:
112109
"""
113110
in addition to the normal functionality, updates the _frame if it exists
114111
"""
115112
self.insert(i, item)
116-
if self._frame is not None:
113+
if 'frame' in self.__dict__:
117114
for value in item:
118-
self._frame.insert(i, column=[value], value=item[value])
115+
self.__dict__['frame'].insert(i, column=[value], value=item[value])
119116

120117
@property
121118
def iloc(self):
@@ -197,8 +194,8 @@ def extend(self, other: "SimResult") -> None:
197194
self.data.extend(other.data)
198195
else:
199196
raise ValueError(f"ValueError: Argument must be of type {self.__class__}")
200-
if self._frame is not None:
201-
self._frame = None
197+
if 'frame' in self.__dict__:
198+
del self.__dict__['frame']
202199

203200
def pop_by_index(self, index: int = -1) -> dict:
204201
"""Remove and return an element
@@ -210,8 +207,8 @@ def pop_by_index(self, index: int = -1) -> dict:
210207
dict: Element Removed
211208
"""
212209
self.times.pop(index)
213-
if self._frame is not None:
214-
self._frame = self._frame.drop([self._frame.index.values[index]])
210+
if 'frame' in self.__dict__:
211+
self.__dict__['frame'].drop([self.__dict__['frame'].index.values[index]], inplace=True)
215212
return self.data.pop(index)
216213

217214
def pop(self, index: int = -1) -> dict:
@@ -248,7 +245,7 @@ def clear(self) -> None:
248245
"""Clear the SimResult"""
249246
self.times = []
250247
self.data = []
251-
self._frame = None
248+
del self.__dict__['frame']
252249

253250
def time(self, index: int) -> float:
254251
"""Get time for data point at index `index`
@@ -364,7 +361,6 @@ def __init__(self, fcn: abc.Callable, times: list = None, states: list = None, _
364361
data (array(dict)): Data points where data[n] corresponds to times[n]
365362
"""
366363
self.fcn = fcn
367-
self.__data = None
368364
if times is None or states is None:
369365
self.times = []
370366
self.states = []
@@ -383,14 +379,14 @@ def is_cached(self) -> bool:
383379
Returns:
384380
bool: If the value has been calculated
385381
"""
386-
return self.__data is not None
382+
return 'data' in self.__dict__
387383

388384
def clear(self) -> None:
389385
"""
390386
Clears the times, states, and data cache for a LazySimResult object
391387
"""
392388
self.times = []
393-
self.__data = None
389+
del self.__dict__['data']
394390
self.states = []
395391

396392
def extend(self, other: "LazySimResult", _copy=True) -> None:
@@ -409,10 +405,13 @@ def extend(self, other: "LazySimResult", _copy=True) -> None:
409405
self.states.extend(deepcopy(other.states))
410406
else:
411407
self.states.extend(other.states)
412-
if self.__data is None or not other.is_cached():
413-
self.__data = None
414-
else:
415-
self.__data.extend(other.data)
408+
if 'data' in self.__dict__: # self is cached
409+
if not other.is_cached():
410+
# Either are not cached
411+
if 'data' in self.__dict__:
412+
del self.__dict__['data']
413+
else:
414+
self.__dict__['data'].extend(other.data)
416415
elif (isinstance(other, SimResult)):
417416
raise ValueError(
418417
f"ValueError: {self.__class__} cannot be extended by SimResult. First convert to SimResult using to_simresult() method.")
@@ -430,8 +429,8 @@ def pop(self, index: int = -1) -> dict:
430429
"""
431430
self.times.pop(index)
432431
x = self.states.pop(index)
433-
if self.__data is not None:
434-
return self.__data.pop(index)
432+
if 'data' in self.__dict__: # is cached
433+
return self.__dict__['data'].pop(index)
435434
return self.fcn(x)
436435

437436
def remove(self, d: float = None, t: float = None, s=None) -> None:
@@ -457,16 +456,12 @@ def remove(self, d: float = None, t: float = None, s=None) -> None:
457456
def to_simresult(self) -> SimResult:
458457
return SimResult(self.times, self.data)
459458

460-
@property
459+
@cached_property
461460
def data(self) -> List[dict]:
462461
"""
463462
Get the data (elements of list). Only calculated on first request
464463
465464
Returns:
466465
array(dict): data
467466
"""
468-
if self.__data is None:
469-
self.__data = [self.fcn(x) for x in self.states]
470-
return self.__data
471-
472-
467+
return [self.fcn(x) for x in self.states]

0 commit comments

Comments
 (0)