ecopipeline 0.8.2__tar.gz → 0.8.4__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.
Files changed (25) hide show
  1. {ecopipeline-0.8.2/src/ecopipeline.egg-info → ecopipeline-0.8.4}/PKG-INFO +1 -1
  2. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/setup.cfg +1 -1
  3. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/event_tracking/event_tracking.py +38 -26
  4. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/extract/extract.py +1 -1
  5. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/load/load.py +2 -1
  6. {ecopipeline-0.8.2 → ecopipeline-0.8.4/src/ecopipeline.egg-info}/PKG-INFO +1 -1
  7. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/LICENSE +0 -0
  8. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/README.md +0 -0
  9. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/pyproject.toml +0 -0
  10. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/setup.py +0 -0
  11. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/__init__.py +0 -0
  12. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/event_tracking/__init__.py +0 -0
  13. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/extract/__init__.py +0 -0
  14. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/load/__init__.py +0 -0
  15. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/transform/__init__.py +0 -0
  16. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/transform/bayview.py +0 -0
  17. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/transform/lbnl.py +0 -0
  18. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/transform/transform.py +0 -0
  19. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/utils/ConfigManager.py +0 -0
  20. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/utils/__init__.py +0 -0
  21. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/utils/unit_convert.py +0 -0
  22. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline.egg-info/SOURCES.txt +0 -0
  23. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline.egg-info/dependency_links.txt +0 -0
  24. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline.egg-info/requires.txt +0 -0
  25. {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ecopipeline
3
- Version: 0.8.2
3
+ Version: 0.8.4
4
4
  Summary: Contains functions for use in Ecotope Datapipelines
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: License :: OSI Approved :: GNU General Public License (GPL)
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = ecopipeline
3
- version = 0.8.2
3
+ version = 0.8.4
4
4
  authors = ["Carlos Bello, <bellocarlos@seattleu.edu>, Emil Fahrig <fahrigemil@seattleu.edu>, Casey Mang <cmang@seattleu.edu>, Julian Harris <harrisjulian@seattleu.edu>, Roger Tram <rtram@seattleu.edu>, Nolan Price <nolan@ecotope.com>"]
5
5
  description = Contains functions for use in Ecotope Datapipelines
6
6
  long_description = file: README.md
@@ -3,7 +3,7 @@ import numpy as np
3
3
  import datetime as dt
4
4
  from ecopipeline import ConfigManager
5
5
 
6
- def flag_boundary_alarms(df: pd.DataFrame, config : ConfigManager, default_fault_time : int = 15, site: str = "") -> pd.DataFrame:
6
+ def flag_boundary_alarms(df: pd.DataFrame, config : ConfigManager, default_fault_time : int = 15, site: str = "", full_days : list = None) -> pd.DataFrame:
7
7
  """
8
8
  Function will take a pandas dataframe and location of alarm information in a csv,
9
9
  and create an dataframe with applicable alarm events
@@ -24,6 +24,8 @@ def flag_boundary_alarms(df: pd.DataFrame, config : ConfigManager, default_fault
24
24
  the fault_time column in Varriable_Names.csv
25
25
  site: str
26
26
  string of site name if processing a particular site in a Variable_Names.csv file with multiple sites. Leave as an empty string if not aplicable.
27
+ full_days : list
28
+ list of pd.Datetimes that should be considered full days here. If set to none, will take any day at all present in df
27
29
 
28
30
  Returns
29
31
  -------
@@ -52,7 +54,8 @@ def flag_boundary_alarms(df: pd.DataFrame, config : ConfigManager, default_fault
52
54
  bounds_df['fault_time'] = default_fault_time
53
55
 
54
56
  idx = df.index
55
- unique_days = pd.to_datetime(pd.Series(idx).dt.normalize().unique())
57
+ if full_days is None:
58
+ full_days = pd.to_datetime(pd.Series(idx).dt.normalize().unique())
56
59
 
57
60
  bounds_df = bounds_df.loc[:, ["variable_name", "high_alarm", "low_alarm", "fault_time", "pretty_name"]]
58
61
  bounds_df.dropna(axis=0, thresh=2, inplace=True)
@@ -69,30 +72,11 @@ def flag_boundary_alarms(df: pd.DataFrame, config : ConfigManager, default_fault
69
72
  upper_mask = df[bound_var] > bounds["high_alarm"]
70
73
  if pd.isna(bounds['fault_time']):
71
74
  bounds['fault_time'] = default_fault_time
72
- for day in unique_days:
73
- next_day = day + pd.Timedelta(days=1)
74
- # low alert
75
- low_filtered_df = lower_mask.loc[(lower_mask.index >= day) & (lower_mask.index < next_day)]
76
- low_consecutive_condition = low_filtered_df.rolling(window=bounds["fault_time"]).min() == 1
77
- if low_consecutive_condition.any():
78
- first_true_index = low_consecutive_condition.idxmax()
79
- adjusted_time = first_true_index - pd.Timedelta(minutes=bounds["fault_time"]-1)
80
- alarm_string = f"Lower bound alarm for {bounds['pretty_name']} (first one at {adjusted_time.strftime('%H:%M')})."
81
- if day in alarms:
82
- alarms[day].append([bound_var, alarm_string])
83
- else:
84
- alarms[day] = [[bound_var, alarm_string]]
85
- # high alert
86
- up_filtered_df = upper_mask.loc[(upper_mask.index >= day) & (upper_mask.index < next_day)]
87
- up_consecutive_condition = up_filtered_df.rolling(window=bounds["fault_time"]).min() == 1
88
- if up_consecutive_condition.any():
89
- first_true_index = up_consecutive_condition.idxmax()
90
- adjusted_time = first_true_index - pd.Timedelta(minutes=bounds["fault_time"]-1)
91
- alarm_string = f"Upper bound alarm for {bounds['pretty_name']} (first one at {adjusted_time.strftime('%H:%M')})."
92
- if day in alarms:
93
- alarms[day].append([bound_var, alarm_string])
94
- else:
95
- alarms[day] = [[bound_var, alarm_string]]
75
+ for day in full_days:
76
+ if bounds['fault_time'] < 1 :
77
+ print(f"Could not process alarm for {bound_var}. Fault time must be greater than or equal to 1 minute.")
78
+ _check_and_add_alarm(df, lower_mask, alarms, day, bounds["fault_time"], bound_var, bounds['pretty_name'], 'Lower')
79
+ _check_and_add_alarm(df, upper_mask, alarms, day, bounds["fault_time"], bound_var, bounds['pretty_name'], 'Upper')
96
80
  events = {
97
81
  'start_time_pt' : [],
98
82
  'end_time_pt' : [],
@@ -112,6 +96,34 @@ def flag_boundary_alarms(df: pd.DataFrame, config : ConfigManager, default_fault
112
96
  event_df.set_index('start_time_pt', inplace=True)
113
97
  return event_df
114
98
 
99
+ def _check_and_add_alarm(df : pd.DataFrame, mask : pd.Series, alarms_dict, day, fault_time : int, var_name : str, pretty_name : str, alarm_type : str = 'Lower'):
100
+ # KNOWN BUG : Avg value during fault time excludes the first (fault_time-1) minutes of each fault window
101
+ next_day = day + pd.Timedelta(days=1)
102
+ filtered_df = mask.loc[(mask.index >= day) & (mask.index < next_day)]
103
+ consecutive_condition = filtered_df.rolling(window=fault_time).min() == 1
104
+ if consecutive_condition.any():
105
+ group = (consecutive_condition != consecutive_condition.shift()).cumsum()
106
+ streaks = consecutive_condition.groupby(group).agg(['sum', 'size', 'idxmin'])
107
+ true_streaks = streaks[consecutive_condition.groupby(group).first()]
108
+ longest_streak_length = true_streaks['size'].max()
109
+ avg_streak_length = true_streaks['size'].mean() + fault_time-1
110
+ longest_group = true_streaks['size'].idxmax()
111
+ streak_indices = consecutive_condition[group == longest_group].index
112
+ starting_index = streak_indices[0]
113
+
114
+ day_df = df.loc[(df.index >= day) & (df.index < next_day)]
115
+ average_value = day_df.loc[consecutive_condition, var_name].mean()
116
+
117
+ # first_true_index = consecutive_condition.idxmax()
118
+ # because first (fault_time-1) minutes don't count in window
119
+ adjusted_time = starting_index - pd.Timedelta(minutes=fault_time-1)
120
+ adjusted_longest_streak_length = longest_streak_length + fault_time-1
121
+ alarm_string = f"{alarm_type} bound alarm for {pretty_name} (longest at {adjusted_time.strftime('%H:%M')} for {adjusted_longest_streak_length} minutes). Avg fault time : {avg_streak_length} minutes, Avg value during fault: {average_value}"
122
+ if day in alarms_dict:
123
+ alarms_dict[day].append([var_name, alarm_string])
124
+ else:
125
+ alarms_dict[day] = [[var_name, alarm_string]]
126
+
115
127
  # def flag_dhw_outage(df: pd.DataFrame, daily_df : pd.DataFrame, dhw_outlet_column : str, supply_temp : int = 110, consecutive_minutes : int = 15) -> pd.DataFrame:
116
128
  # """
117
129
  # Parameters
@@ -760,7 +760,7 @@ def tb_api_to_df(config: ConfigManager, startTime: datetime = None, endTime: dat
760
760
  # 28 hours to ensure encapsulation of last day
761
761
  startTime = endTime - timedelta(hours=28)
762
762
 
763
- if endTime - timedelta(hours=4) > startTime:
763
+ if endTime - timedelta(hours=12) > startTime:
764
764
  time_diff = endTime - startTime
765
765
  midpointTime = startTime + time_diff / 2
766
766
  # recursively construct the df
@@ -385,8 +385,9 @@ def load_event_table(config : ConfigManager, event_df: pd.DataFrame, site_name :
385
385
  (existing_rows['event_type'] == row['event_type'])
386
386
  ]
387
387
  if not time_data[-1] is None and not filtered_existing_rows.empty:
388
+ # silent alarm only
388
389
  filtered_existing_rows = filtered_existing_rows[(filtered_existing_rows['variable_name'] == row['variable_name']) &
389
- (filtered_existing_rows['event_detail'] == row['event_detail'])]
390
+ (filtered_existing_rows['event_detail'].str[:20] == row['event_detail'][:20])] # the [:20] part is a bug fix for partial days for silent alarms
390
391
 
391
392
  if not filtered_existing_rows.empty:
392
393
  first_matching_row = filtered_existing_rows.iloc[0] # Retrieves the first row
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ecopipeline
3
- Version: 0.8.2
3
+ Version: 0.8.4
4
4
  Summary: Contains functions for use in Ecotope Datapipelines
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: License :: OSI Approved :: GNU General Public License (GPL)
File without changes
File without changes
File without changes
File without changes