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.
- {ecopipeline-0.8.2/src/ecopipeline.egg-info → ecopipeline-0.8.4}/PKG-INFO +1 -1
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/setup.cfg +1 -1
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/event_tracking/event_tracking.py +38 -26
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/extract/extract.py +1 -1
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/load/load.py +2 -1
- {ecopipeline-0.8.2 → ecopipeline-0.8.4/src/ecopipeline.egg-info}/PKG-INFO +1 -1
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/LICENSE +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/README.md +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/pyproject.toml +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/setup.py +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/__init__.py +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/event_tracking/__init__.py +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/extract/__init__.py +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/load/__init__.py +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/transform/__init__.py +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/transform/bayview.py +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/transform/lbnl.py +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/transform/transform.py +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/utils/ConfigManager.py +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/utils/__init__.py +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline/utils/unit_convert.py +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline.egg-info/SOURCES.txt +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline.egg-info/dependency_links.txt +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline.egg-info/requires.txt +0 -0
- {ecopipeline-0.8.2 → ecopipeline-0.8.4}/src/ecopipeline.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[metadata]
|
|
2
2
|
name = ecopipeline
|
|
3
|
-
version = 0.8.
|
|
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
|
-
|
|
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
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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=
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|