'Triggering an Action on the Change of an Attribute of an Attribute
I have an object called Data, which has a pandas DataFrame (df) as an attribute. What I want to do is trigger an action on the change of that df. Specifically, the addition of a new column.
Right now I have it working by re-setting the attribute, but it doesn't catch any changes to its attributes. Here is an example:
class Data:
def __init__(self, df: Optional[DataFrame] = None) -> None:
self.sets = 0
self.updates = 0
self._df = df if df is not None else pd.DataFrame
@property
def df(self) -> DataFrame:
return self._df
@df.setter
def df(self, new_df: DataFrame) -> None:
if self._df.empty:
self.sets += 1
# do something...
else:
self.updates += 1
# do somthing else...
self._df = new_df.copy(deep=True)
Behavior
Init
d = Data()
# d.sets == 0
# d.updates == 0
Set df
d.df = pd.DataFrame({
'a': [1, 1, 1],
'b': [2, 2, 2],
})
# d.sets == 1
# d.updates == 0
Update df
d.df = pd.DataFrame({
'a': [1, 1, 1],
'b': [2, 2, 2],
'c': [3, 3, 3],
})
# d.sets == 1
# d.updates == 1
Add New Column (NOT WORKING)
d.df['d'] = [4, 4, 4]
# d.sets == 1
# d.updates == 1 (EXPECTED 2)
Solution 1:[1]
One way to do it is to define a helper method to get both sets and updates count instead of accessing attributes directly, like this:
from typing import Optional
import pandas as pd
from pandas.testing import assert_frame_equal
class Data:
def __init__(self, df: Optional[pd.DataFrame] = None) -> None:
self.sets = 0
self.updates = 0
self._df = df or pd.DataFrame # you don't need an if/else expression
self.previous = self._df # new attribute to track last known _df
@property
def df(self) -> pd.DataFrame:
return self._df
@df.setter
def df(self, new_df: pd.DataFrame) -> None:
if self._df.empty:
self.sets += 1
# do something...
else:
self.updates += 1
# do somthing else...
self._df = new_df.copy(deep=True)
self.previous = new_df.copy(deep=True)
@property
def count_changes(self) -> dict[str, int]:
# check if _df has been modified
try:
assert_frame_equal(self._df, self.previous)
except AssertionError:
self.updates += 1
return {"sets": self.sets, "updates": self.updates}
So that:
d = Data()
d.df = pd.DataFrame(
{
"a": [1, 1, 1],
"b": [2, 2, 2],
}
)
d.df = pd.DataFrame(
{
"a": [1, 1, 1],
"b": [2, 2, 2],
"c": [3, 3, 3],
}
)
d.df["d"] = [4, 4, 4]
print(d.count_changes)
# Output
{'sets': 1, 'updates': 2} # as expected
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Laurent |
