Peopulse / phase /compute.py
sdbrgo's picture
Update phase/compute.py
b84e820 verified
import math
class ParticipationAdoptionIndex:
def __init__(self, num_participants, target_population, feedback_volume):
self.num_participants = num_participants
self.target_population = target_population
self.feedback_volume = feedback_volume
self.components_1 = {}
self.components_2 = {}
# ---------- helper function(s) ----------
def _compute_pci(self, participants_by_group):
total = sum(participants_by_group.values())
if total == 0:
return 0.0
shares = [
count / total
for count in participants_by_group.values()
]
return sum(s ** 2 for s in shares)
# ---------- public API ----------
def compute_pai(self, participants_by_group=None):
# input validation for 'participants_by_group'
self.num_participants = sum(participants_by_group.values()) if participants_by_group else num_participants
# absolute counts
spi_a_abs = self.target_population - self.num_participants
spi_b_abs = self.num_participants - self.feedback_volume
# ----- 'Participation Dynamics' components -----
pcr = self.num_participants / self.target_population
spi_a = spi_a_abs / self.target_population
spi_b = spi_b_abs / self.num_participants
peg = spi_b_abs / self.target_population
# ----- 'Reach and Equity' components -----
pci = self._compute_pci(participants_by_group) if participants_by_group else 0.0
effective_groups = 1.0 / pci if pci > 0 else 0.0
effective_groups_int = int(math.floor(effective_groups))
effective_groups_rounded = int(round(effective_groups))
# ----- percent-based values -----
final_pcr = round(pcr * 100, 2)
final_spi_a = round(spi_a * 100, 2)
final_spi_b = round(spi_b * 100, 2)
final_peg = round((pcr - peg) / pcr * 100 if pcr > 0 else 0.0, 2)
final_pci = round(pci, 2)
# ----- pci pivot value -----
pci_mid = 0.5
# ---------------------------
self.components_1 = {
"PCR (Participation Coverage Ratio)": {"Value": final_pcr, "Description": f"{final_pcr}% of the target population have participated."},
"SPI-A (Silent Participation Inference - Silent Non-Adoption)": {"Value": final_spi_a, "Description": f"{final_spi_a}% of the target population were not reached or excluded."},
"SPI-B (Silent Participation Inference - Silent Adoption)": {"Value": final_spi_b, "Description": f"{final_spi_b}% of the participants have participated silently."},
"PEG (Participation-to-Expression Gap)": {"Value": final_peg, "Description": f"There is a {final_peg}% gap between participation and expressed feedback."}
}
self.components_2 = {
"PCI (Participation Concentration Index)":
{"Value": final_pci,
"Description": (
f"A PCI of {final_pci} suggests "
f"{'high' if final_pci >= pci_mid else 'low'} participation concentration, "
f"{'dominated by fewer groups' if final_pci >= pci_mid else 'with participation spread across groups'}."
)},
"Group Summary":
{"Minimum Participating Groups": f"At least {effective_groups_int} groups meaningfully participated.",
"Effective Participation Groups": f"Participation is roughly equivalent to {effective_groups_rounded} equally sized groups."
}
}
return self.components_1, self.components_2