File size: 3,687 Bytes
a076a20 3bd086a c614d3b 3bd086a 262d143 3bd086a 91f2cd2 3bd086a dbaf3b6 91f2cd2 3bd086a 9de6162 3bd086a 6f04998 3bd086a d46b5b7 252112f 91f2cd2 6f04998 91f2cd2 49627a9 252112f 49627a9 67900c2 49627a9 8e56998 262d143 d46b5b7 49627a9 d46b5b7 b84e820 3bd086a 262d143 6f04998 91f2cd2 49627a9 2271fb9 6f04998 7d45817 6f04998 262d143 1b64601 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | 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":
{"Major Contributing Groups": f"At least {effective_groups_int} groups made major contributions.",
"Equivalent Equal-Sized Groups": f"Overall participation feels roughly equivalent to {effective_groups_rounded} equally sized groups."
}
}
return self.components_1, self.components_2 |