dynamics-persona-explorer / dynamics_rules.py
JasonDDuke's picture
Update Kronaxis DYNAMICS Persona Explorer Space
a5807e6 verified
Raw
History Blame Contribute Delete
4.42 kB
"""
Deterministic DYNAMICS-to-behaviour mappings.
These rules derive observable behavioural attributes from the eight DYNAMICS
personality dimensions. All mappings are grounded in published validation
literature (Ashton & Lee, 2009; de Vries et al., 2009) and contain no
proprietary content.
Dimension key:
D = Discipline, Y = Yielding, N = Novelty, A = Acuity,
M = Mercuriality, I = Impulsivity, C = Candour, S = Sociability
"""
from __future__ import annotations
def income_band(C: float, D: float) -> str:
"""Predict income band from Candour and Discipline.
Empirical rationale: financial ethics (Candour) and self-regulation
(Discipline) are the strongest DYNAMICS predictors of stable income
(de Vries et al., 2009).
"""
score = C * 0.4 + D * 0.6
if score > 0.75:
return "wealthy"
if score > 0.55:
return "high"
if score > 0.35:
return "mid"
return "low"
def financial_anxiety(M: float, balance: float = 0.0, income: float = 1.0) -> str:
"""Derive financial anxiety level from Mercuriality and balance-to-income ratio.
Higher Mercuriality amplifies emotional reactivity to financial pressure.
A low balance-to-income ratio compounds the effect.
"""
ratio = balance / income if income > 0 else 0.0
base = M * 0.7 + (1.0 - min(ratio, 1.0)) * 0.3
if base > 0.65:
return "high"
if base > 0.35:
return "moderate"
return "low"
def risk_tolerance(M: float, I: float) -> str:
"""Derive risk tolerance from Mercuriality (inverse) and Impulsivity.
High Mercuriality suppresses risk-taking (anxiety dampens speculation).
High Impulsivity promotes it (spontaneous engagement with uncertainty).
"""
score = (1.0 - M) * 0.5 + I * 0.5
if score > 0.65:
return "speculative"
if score > 0.35:
return "balanced"
return "conservative"
def spending_keywords(S: float, I: float, N: float) -> list[str]:
"""Generate spending-style keywords from Sociability, Impulsivity, and Novelty."""
keywords: list[str] = []
if I > 0.6:
keywords.append("impulsive")
elif I < 0.4:
keywords.append("deliberate")
if S > 0.6:
keywords.append("social-buyer")
if N > 0.6:
keywords.append("novelty-seeking")
elif N < 0.4:
keywords.append("conservative")
return keywords or ["balanced"]
# ---------------------------------------------------------------------------
# Income-band lookup tables
# ---------------------------------------------------------------------------
_INCOME_BAND_RANGES: dict[str, tuple[float, float]] = {
"low": (1200.0, 1800.0),
"mid": (2000.0, 3200.0),
"high": (3500.0, 5500.0),
"wealthy": (6000.0, 12000.0),
}
def default_income_for_band(band: str) -> float:
"""Return the midpoint monthly income for a given income band."""
lo, hi = _INCOME_BAND_RANGES.get(band, (2000.0, 3200.0))
return (lo + hi) / 2.0
def derive_attributes(
dynamics: dict[str, float],
balance: float | None = None,
income: float | None = None,
) -> dict:
"""Compute all derived behavioural attributes from a DYNAMICS vector.
Parameters
----------
dynamics : dict
Keys D, Y, N, A, M, I, C, S with float values in [0, 1].
balance : float or None
Current account balance in GBP. If None, defaults to half of
the derived monthly income.
income : float or None
Monthly income in GBP. If None, derived from income band.
Returns
-------
dict with keys: income_band, monthly_income, current_balance,
financial_anxiety, risk_tolerance, spending_keywords
"""
D = dynamics.get("D", 0.5)
Y = dynamics.get("Y", 0.5)
N = dynamics.get("N", 0.5)
A = dynamics.get("A", 0.5)
M = dynamics.get("M", 0.5)
I = dynamics.get("I", 0.5)
C = dynamics.get("C", 0.5)
S = dynamics.get("S", 0.5)
band = income_band(C, D)
monthly = income if income is not None else default_income_for_band(band)
bal = balance if balance is not None else monthly * 0.5
return {
"income_band": band,
"monthly_income": round(monthly, 2),
"current_balance": round(bal, 2),
"financial_anxiety": financial_anxiety(M, bal, monthly),
"risk_tolerance": risk_tolerance(M, I),
"spending_keywords": spending_keywords(S, I, N),
}