Skip to content

Commit 80ad487

Browse files
authored
359 add additional messages goal worse approach maybe sustain (#380)
* goal worse added * approach signal added and used to process approach causal pathways and messages
1 parent 994816d commit 80ad487

File tree

8 files changed

+263
-53
lines changed

8 files changed

+263
-53
lines changed

bitstomach/signals/__init__.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,14 @@ def exclude(cls, mi, types: List[Resource]) -> bool:
7474
from bitstomach.signals._trend import Trend # noqa: E402, I001
7575
from bitstomach.signals._achievement import Achievement # noqa: E402, I001
7676
from bitstomach.signals._loss import Loss # noqa: E402, I001
77+
from bitstomach.signals._approach import Approach # noqa: E402, I001
7778

78-
__all__ = ["Comparison", "Trend", "Achievement", "Loss"]
79+
__all__ = ["Comparison", "Trend", "Achievement", "Loss", "Approach"]
7980

80-
SIGNALS = {Comparison: Signal, Trend: Signal, Achievement: Signal, Loss: Signal}
81+
SIGNALS = {
82+
Comparison: Signal,
83+
Trend: Signal,
84+
Achievement: Signal,
85+
Loss: Signal,
86+
Approach: Signal,
87+
}

bitstomach/signals/_approach.py

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
from typing import List, Optional
2+
3+
import numpy as np
4+
import pandas as pd
5+
from rdflib import RDF, Literal
6+
from rdflib.resource import Resource
7+
8+
from bitstomach.signals import Comparison, Signal, Trend
9+
from utils.namespace import PSDO, SLOWMO
10+
11+
12+
class Approach(Signal):
13+
signal_type = PSDO.approach_content
14+
15+
@staticmethod
16+
def detect(perf_data: pd.DataFrame) -> Optional[List[Resource]]:
17+
if perf_data.empty:
18+
raise ValueError
19+
20+
trend_signals = Trend.detect(perf_data)
21+
if (
22+
not trend_signals
23+
or not trend_signals[0][RDF.type : PSDO.positive_performance_trend_content]
24+
):
25+
return []
26+
27+
negative_comparison_signals = [
28+
s
29+
for s in Comparison.detect(perf_data)
30+
if s[RDF.type : PSDO.negative_performance_gap_content]
31+
]
32+
33+
negative_prior_month_comparisons = [
34+
s
35+
for s in Comparison.detect(perf_data.iloc[:-1], tiered_comparators=False)
36+
if s[RDF.type : PSDO.negative_performance_gap_content]
37+
]
38+
39+
approach_signals = []
40+
41+
for comparison_signal in negative_comparison_signals:
42+
previous_comparison_signal = next(
43+
(
44+
comparison
45+
for comparison in negative_prior_month_comparisons
46+
if (
47+
Comparison.comparator_type(comparison)
48+
== Comparison.comparator_type(comparison_signal)
49+
)
50+
),
51+
None,
52+
)
53+
54+
if not previous_comparison_signal:
55+
continue
56+
57+
streak_length = Approach._detect(
58+
perf_data, comparison_signal.value(SLOWMO.RegardingComparator)
59+
)
60+
61+
mi = Approach._resource(
62+
trend_signals[0],
63+
comparison_signal,
64+
previous_comparison_signal,
65+
streak_length,
66+
)
67+
68+
approach_signals.append(mi)
69+
return approach_signals
70+
71+
@classmethod
72+
def _resource(
73+
cls,
74+
trend_signal: Resource,
75+
comparison_signal: Resource,
76+
previous_comparison_signal: Resource,
77+
streak_length: int,
78+
) -> Resource:
79+
# create and type the Achievmente
80+
mi = super()._resource()
81+
mi.add(RDF.type, Comparison.signal_type)
82+
mi.add(RDF.type, Trend.signal_type)
83+
84+
# set signal properties
85+
mi[SLOWMO.PerformanceTrendSlope] = trend_signal.value(
86+
SLOWMO.PerformanceTrendSlope
87+
)
88+
mi[SLOWMO.PerformanceGapSize] = comparison_signal.value(
89+
SLOWMO.PerformanceGapSize
90+
)
91+
mi[SLOWMO.PriorPerformanceGapSize] = previous_comparison_signal.value(
92+
SLOWMO.PerformanceGapSize
93+
)
94+
mi[SLOWMO.StreakLength] = Literal(streak_length)
95+
96+
# add comparator (Achievments are a Comparison)
97+
comparator = comparison_signal.value(SLOWMO.RegardingComparator)
98+
99+
mi[SLOWMO.RegardingComparator] = comparator
100+
101+
g = mi.graph
102+
g += comparison_signal.graph.triples((comparator.identifier, None, None))
103+
104+
return mi
105+
106+
@classmethod
107+
def disposition(cls, mi: Resource) -> List[Resource]:
108+
dispos = super().disposition(mi)
109+
dispos += Comparison.disposition(mi)
110+
dispos += Trend.disposition(mi)
111+
112+
return dispos
113+
114+
@classmethod
115+
def moderators(cls, motivating_informations: List[Resource]) -> List[dict]:
116+
"""
117+
extracts approach moderators (trend_slope, gap_size, comparator_type and prior_gap_size) from a suplied list of motivating information
118+
"""
119+
mods = []
120+
121+
for signal in super().select(motivating_informations):
122+
motivating_info_dict = super().moderators(signal)
123+
124+
motivating_info_dict["gap_size"] = round(
125+
abs(signal.value(SLOWMO.PerformanceGapSize).value), 4
126+
)
127+
motivating_info_dict["comparator_type"] = signal.value(
128+
SLOWMO.RegardingComparator / RDF.type
129+
).identifier
130+
motivating_info_dict["trend_size"] = round(
131+
abs(signal.value(SLOWMO.PerformanceTrendSlope).value * 2), 4
132+
)
133+
motivating_info_dict["prior_gap_size"] = round(
134+
abs(signal.value(SLOWMO.PriorPerformanceGapSize).value), 4
135+
)
136+
motivating_info_dict["streak_length"] = (
137+
signal.value(SLOWMO.StreakLength).value / 12
138+
)
139+
140+
mods.append(motivating_info_dict)
141+
142+
return mods
143+
144+
@staticmethod
145+
def _detect(perf_data: pd.DataFrame, comparator: Resource) -> float:
146+
"""
147+
calculates the number of consecutive negative gaps prior to this months negative gap.
148+
"""
149+
comp_cols = {
150+
PSDO["peer_average_comparator"]: "peer_average_comparator",
151+
PSDO["peer_75th_percentile_benchmark"]: "peer_75th_percentile_benchmark",
152+
PSDO["peer_90th_percentile_benchmark"]: "peer_90th_percentile_benchmark",
153+
PSDO["goal_comparator_content"]: "goal_comparator_content",
154+
}
155+
156+
comparator_id = comparator.value(RDF.type).identifier
157+
158+
gaps = perf_data["passed_rate"] - perf_data[comp_cols[comparator_id]] / 100
159+
160+
# find the number of consecutive negative gaps
161+
diff_reversed = gaps.values[:-1][::-1]
162+
end_negative_gaps_index = np.argmax(diff_reversed >= 0)
163+
if end_negative_gaps_index == 0:
164+
consecutive_negative_gaps = len(diff_reversed)
165+
else:
166+
consecutive_negative_gaps = end_negative_gaps_index
167+
168+
return consecutive_negative_gaps

candidate_pudding/candidate_pudding.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ def add_causal_pathway(candidate: Resource):
9595
"Achieved Top 10 Peer Benchmark": "social gain",
9696
"No Longer Top Performer": "social loss",
9797
"Drop Below Peer Average": "social loss",
98+
"Opportunity to Improve Goal": "goal worse",
99+
"Approach Peer Average": "social approach",
100+
"Approach Top 10 Peer Benchmark": "social approach",
101+
"Approach Top 25 Peer Benchmark": "social approach",
102+
"Approach Goal": "goal approach",
98103
}
99104
ancestor_template = candidate.value(SLOWMO.AncestorTemplate)
100105
template_name = ancestor_template.value(URIRef("http://schema.org/name")).value

0 commit comments

Comments
 (0)