Observed since 20 Oct, 21:11 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
afrikana-analytics
Production-ready analytics toolkit for customer retention, LTV, financial modelling, and infrastructure deployment - built in Africa, applicable anywhere
Production-ready analytics toolkit for any operator managing customers, infrastructure, and growth decisions at scale
Built from real analytical work across African markets. Applicable to any subscription, usage-based, or network-deployment business — mobility, fintech, telecoms, utilities, logistics, or SaaS.
Covers the full data-to-decision stack: churn prediction, customer lifetime value, financial modelling, site deployment optimisation, and demand forecasting.
# From GitHub Packages
pip install afrikana-analytics
# From source
git clone https://github.com/Peterson-Muriuki/afrikana-analytics
cd afrikana-analytics
pip install -e .
from afrikana.churn import ChurnScorer
from afrikana.ltv import LTVCalculator
from afrikana.financial import FinancialModel
from afrikana.stations import StationOptimizer
from afrikana.forecast import DemandForecaster
# --- Churn prediction ---
scorer = ChurnScorer()
scorer.fit(customers_df)
at_risk = scorer.at_risk(customers_df, threshold=0.5)
print(f"At-risk customers: {len(at_risk)}")
print(scorer.feature_importances())
# --- Customer LTV ---
calc = LTVCalculator(gross_margin=0.62)
result = calc.compute(customers_df)
print(calc.tier_summary(result))
print(calc.revenue_at_risk(result))
# --- Station financial model ---
model = FinancialModel(swap_price_usd=2.50, n_stations=20)
print(model.unit_economics())
print(model.breakeven())
print(model.dcf())
print(model.scenarios())
mc = model.monte_carlo(n_sims=2000)
print(f"NPV P50: ${mc['npv_p50']:,.0f} Prob +ve NPV: {mc['prob_positive_npv']}%")
# --- Deployment optimisation ---
opt = StationOptimizer()
candidates = opt.generate_grid((-1.286389, 36.817223), n=40)
scored = opt.score(candidates, existing_stations_df)
print(opt.recommend(scored, top_n=5))
# --- Demand forecasting ---
fc = DemandForecaster()
daily = fc.prepare_daily(swap_events_df)
forecast = fc.predict(daily, periods=30)
print(fc.forecast_summary(forecast))
ChurnScorerGradient Boosting churn predictor for subscription/usage-based mobility businesses.
from afrikana.churn import ChurnScorer, ChurnScorerConfig
config = ChurnScorerConfig(n_estimators=200, verbose=True)
scorer = ChurnScorer(config)
scorer.fit(df) # trains and evaluates on held-out split
scored = scorer.score(df) # adds churn_score [0-1] and churn_risk tier
at_risk = scorer.at_risk(df, 0.6) # customers above threshold, sorted
scorer.feature_importances() # what drives churn most
print(scorer.summary()) # {"auc": 0.82, "top_feature": "last_swap_days_ago", ...}
Required columns: swap_freq_monthly, last_swap_days_ago, tenure_months, monthly_revenue, churned (0/1 target)
LTVCalculatorDiscounted survival-adjusted Customer Lifetime Value with Bronze/Silver/Gold tiers.
from afrikana.ltv import LTVCalculator
calc = LTVCalculator(gross_margin=0.62, discount_rate_annual=0.12, max_horizon_months=36)
df = calc.compute(customers_df) # adds ltv, ltv_tier, expected_lifetime
calc.tier_summary(df) # count, avg_ltv, total_ltv per tier
calc.segment_summary(df, "country") # LTV by country / segment / city
calc.revenue_at_risk(df, threshold=0.5)
Required columns: churn_probability (or churn_score), monthly_revenue, tenure_months
FinancialModelFull financial model for an EV swap station network.
from afrikana.financial import FinancialModel
model = FinancialModel(
swap_price_usd=2.50,
swaps_per_station_day=18,
n_stations=20,
gross_margin_pct=0.62,
discount_rate_annual=0.12,
)
model.unit_economics() # per-station P&L: revenue, EBITDA, NOPAT, contribution/swap
model.pl_projection() # 36-month network P&L as DataFrame
model.cash_flow() # operating CF, capex, FCF, cumulative cash
model.breakeven() # swaps/day needed, margin of safety, payback period
model.dcf() # NPV, IRR, ROI, terminal value
model.scenarios() # Base / Bull / Bear comparison table
model.monte_carlo(2000) # P10/P50/P90 NPV distribution, VaR 95%, prob +ve NPV
StationOptimizerMulti-criteria deployment scorer for new swap station locations.
from afrikana.stations import StationOptimizer, OptimizerConfig
config = OptimizerConfig(
weight_demand_density=0.30,
weight_coverage_gap=0.25,
weight_revenue_potential=0.25,
weight_underserved=0.20,
)
opt = StationOptimizer(config)
candidates = opt.generate_grid((-1.286, 36.817), n=40) # synthetic grid
scored = opt.score(candidates, existing_stations_df)
top5 = opt.recommend(scored, top_n=5)
stats = opt.coverage_stats(scored)
Required columns in existing_stations_df: lat, lon, status
DemandForecasterHolt-Winters time-series forecaster with confidence intervals.
from afrikana.forecast import DemandForecaster
fc = DemandForecaster(seasonal_periods=7)
daily = fc.prepare_daily(swap_events_df)
monthly = fc.prepare_monthly(swap_events_df)
forecast = fc.predict(daily, periods=30) # date, forecast, lower, upper
summary = fc.forecast_summary(forecast)
peaks = fc.peak_hours(swap_events_df) # 24-row hour-of-day breakdown
pip install -e .
python examples/spiro_demo.py
pip install -e ".[dev]"
pytest tests/ -v --cov=afrikana
Built for and tested against data patterns from:
| Country | Cities | Currency |
|---|---|---|
| Kenya | Nairobi, Mombasa, Kisumu, Nakuru | KES |
| Nigeria | Lagos, Abuja, Kano, Port Harcourt | NGN |
| Rwanda | Kigali, Butare, Gisenyi | RWF |
| Uganda | Kampala, Entebbe, Jinja | UGX |
| Ghana | Accra, Kumasi, Tamale | GHS |
| Ethiopia | Addis Ababa, Dire Dawa | ETB |
Peterson Mutegi — Data Analyst · AI Engineer · Financial Engineer
Nairobi, Kenya · pitmuriuki@gmail.com
GitHub · [LinkedIn
Built on top of real analytical work for African EV mobility operations.
MIT — see LICENSE for details.
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
pandasEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pandas>=2.0.0 | runtime |
numpyEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | numpy>=1.24.0 | runtime |
scikit-learnEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | scikit-learn>=1.3.0 | runtime |
statsmodelsEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | statsmodels>=0.14.0 | runtime |
scipyEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | scipy>=1.11.0 | runtime |
pytestEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pytest>=7.4.0; extra == "dev" | runtime |
pytest-covEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pytest-cov>=4.1.0; extra == "dev" | runtime |
blackEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | black>=23.0.0; extra == "dev" | runtime |
ruffEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | ruff>=0.1.0; extra == "dev" | runtime |
plotlyEnvironment{
"extras": "",
"marker": "extra == \"viz\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | plotly>=5.18.0; extra == "viz" | runtime |
matplotlibEnvironment{
"extras": "",
"marker": "extra == \"viz\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | matplotlib>=3.7.0; extra == "viz" | runtime |
bdist_wheel · 20.46 KB · Python py3
sdist · 23.12 KB · Python source
Shared repository data · GitHub
Observed since 20 Oct, 21:11 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:511367d3a856ed7c026da7186aaf5294d2f21d99e239b57b7a28673ad56041d0