{"id":50676878,"url":"https://github.com/ash-datapro/churn-pred","last_synced_at":"2026-06-08T16:01:54.736Z","repository":{"id":323596981,"uuid":"1093893626","full_name":"ash-datapro/churn-pred","owner":"ash-datapro","description":"A production-style, end-to-end churn prediction stack.","archived":false,"fork":false,"pushed_at":"2025-12-15T01:05:11.000Z","size":67035,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-17T16:18:07.277Z","etag":null,"topics":["analytics","api","churn","dashboard","docker","machine-learning","plumber","postgresql","r","shiny","tidymodels","xgboost"],"latest_commit_sha":null,"homepage":"https://raw.githubusercontent.com/ash-datapro/post-sales-churn-pred/main/media/demo.gif","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ash-datapro.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-11T01:32:43.000Z","updated_at":"2025-12-15T01:05:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ash-datapro/churn-pred","commit_stats":null,"previous_names":["ash-datapro/post-sales-churn-pred","ash-datapro/churn-pred"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/ash-datapro/churn-pred","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ash-datapro%2Fchurn-pred","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ash-datapro%2Fchurn-pred/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ash-datapro%2Fchurn-pred/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ash-datapro%2Fchurn-pred/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ash-datapro","download_url":"https://codeload.github.com/ash-datapro/churn-pred/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ash-datapro%2Fchurn-pred/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34069501,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["analytics","api","churn","dashboard","docker","machine-learning","plumber","postgresql","r","shiny","tidymodels","xgboost"],"created_at":"2026-06-08T16:01:50.130Z","updated_at":"2026-06-08T16:01:54.730Z","avatar_url":"https://github.com/ash-datapro.png","language":"R","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Post-Sales Customer Churn (R + Shiny + Plumber)\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"media/demo.gif\" width=\"100%\" alt=\"App demo\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#\"\u003e\u003cimg src=\"https://img.shields.io/badge/R-%3E%3D4.3-blue\"\u003e\u003c/a\u003e\n  \u003ca href=\"#\"\u003e\u003cimg src=\"https://img.shields.io/badge/Shiny-UI-%2300ADD8\"\u003e\u003c/a\u003e\n  \u003ca href=\"#\"\u003e\u003cimg src=\"https://img.shields.io/badge/plumber-API-%23E76F51\"\u003e\u003c/a\u003e\n  \u003ca href=\"#\"\u003e\u003cimg src=\"https://img.shields.io/badge/PostgreSQL-%3E%3D13-%234169E1\"\u003e\u003c/a\u003e\n  \u003ca href=\"#\"\u003e\u003cimg src=\"https://img.shields.io/badge/tidymodels-xgboost-success\"\u003e\u003c/a\u003e\n  \u003ca href=\"#\"\u003e\u003cimg src=\"https://img.shields.io/badge/Docker-optional-lightgrey\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n**A production-style, end-to-end churn analytics project**:\n\n* **Frontend**: Dark-themed Shiny dashboard with filters, KPIs, insights, and a live prediction form.\n* **Backend**: `plumber` REST API serving a tidymodels gradient boosting model.\n* **Data**: PostgreSQL with reproducible load/feature engineering scripts.\n* **Reports**: Confusion matrix, ROC curve, and a markdown report for the trained model.\n\n---\n\n## Table of Contents\n\n* [Architecture](#architecture)\n* [Features](#features)\n* [Screenshots](#screenshots)\n* [Getting Started](#getting-started)\n\n  * [Prerequisites](#prerequisites)\n  * [Repository Layout](#repository-layout)\n  * [Environment](#environment)\n  * [1) Load Data](#1-load-data)\n  * [2) Train Model](#2-train-model)\n  * [3) Run the API](#3-run-the-api)\n  * [4) Run the Shiny App](#4-run-the-shiny-app)\n* [API](#api)\n* [Usage Notes \u0026 Tips](#usage-notes--tips)\n* [Troubleshooting](#troubleshooting)\n* [Roadmap](#roadmap)\n* [Contributing](#contributing)\n* [License](#license)\n\n---\n\n## Architecture\n\n```\nPostgreSQL  ←  data/load-data.R\n      ↑\n      │                   ┌──────────────────────────────┐\n      │                   │        Shiny Frontend        │\n      │                   │  KPIs, filters, insights,    │\n      │                   │  predictions (dark theme)    │\n      │                   └──────────────┬───────────────┘\n      │                                  │ HTTP (JSON)\n      │                           /predict (plumber)\n      │                                  │\n      └──────────  backend/api/train-model.R  ──►  model.rds\n```\n\n* **Modeling**: tidymodels workflow with `xgboost` tuning, F1-optimized threshold, saved as `model.rds`.\n* **API**: `plumber` exposes `/health` and `/predict`, loading `model.rds`.\n* **UI**: Shiny dashboard (dark, compact), filterable, with insights plots and a guided prediction form.\n\n---\n\n## Features\n\n* **Interactive dashboard**: KPIs, churn mix, tenure/charges visuals, contract vs churn, “quick insights” chips, and a downloadable filtered CSV.\n* **Live predictions**: Enter customer attributes → get class \u0026 probability from the API.\n* **Reproducible training**: Train script generates reports (`reports/`) and the serialized model.\n* **Database-backed**: Load Telco churn CSV into Postgres and query from the app.\n\n---\n\n## Screenshots\n\n* Dashboard (dark mode), filters \u0026 KPIs \u003cimg src=\"media/demo.gif\" width=\"100%\" alt=\"Demo\"\u003e\n\n---\n\n## Getting Started\n\n### Prerequisites\n\n* **R** ≥ 4.3 with packages listed in `frontend/R/requirements.txt` and `backend/api/requirements.txt` (if you maintain one there).\n* **PostgreSQL** ≥ 13\n* **Optional**: Docker / docker-compose (a `docker-compose.yml` is included but local run works fine).\n\n\u003e R style in this repo uses `=` for assignment.\n\n### Repository Layout\n\n```\nbackend/\n  api/\n    main.R            # plumber API (serves /health, /predict)\n    train-model.R     # tidymodels + xgboost training\n    model.rds         # trained model artifact\n  reports/            # confusion matrix, ROC, report.md\n\ndata/\n  load-data.R         # create schema \u0026 load Telco CSV into Postgres\n  Telco-Customer-Churn.csv\n\nfrontend/\n  app.R               # Shiny app entry\n  R/\n    about.R           # About tab (cards)\n    insights.R        # Correlation / feature-importance\n    metrics.R         # KPI value boxes\n    plots.R           # Plot helpers + dark theming\n    prediction.R      # Prediction form + API call\n    retrain.R         # (optional) hooks to retrain\n    sidebar.R         # Filters module (selectize + slider)\n    utils.R           # helpers\n\npostgres/\n  create_schema.sql   # optional schema helper\n\nmedia/\n  demo.gif\n```\n\n### Environment\n\nCreate a local `.Renviron` (or export in shell) with your DB settings:\n\n```sh\nexport DB_USER=\"user\"\nexport DB_PASSWORD=\"password\"\nexport DB_HOST=\"localhost\"   # or 'postgres' if running under docker-compose\nexport DB_PORT=\"5432\"\nexport DB_NAME=\"churn_db\"\n```\n\n\u003e In RStudio, you can also use **Tools → Global Options → Environment** or put these in `~/.Renviron`.\n\n### 1) Load Data\n\n```r\n# from repo root or the data/ folder\nsource(\"data/load-data.R\")\n# This reads Telco-Customer-Churn.csv and populates the 'customers' table.\n```\n\n### 2) Train Model\n\n```r\n# from backend/api/\nsetwd(\"backend/api\")\nsource(\"train-model.R\")\n# Outputs:\n# - backend/api/model.rds\n# - backend/reports/{confusion_matrix.png, roc_curve.png, classification_report.txt, model_report.md}\n```\n\n### 3) Run the API\n\n```r\n# from backend/api/\nsetwd(\"backend/api\")\nlibrary(plumber)\npr = plumb(\"main.R\")\npr$run(host = \"0.0.0.0\", port = 8000)\n# Swagger UI: http://127.0.0.1:8000/__docs__/\n```\n\nSanity test:\n\n```sh\ncurl -X POST \"http://127.0.0.1:8000/predict\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"tenure\": 15,\n    \"monthly_charges\": 70,\n    \"contract\": \"Month-to-month\",\n    \"gender\": \"Female\",\n    \"senior_citizen\": 0,\n    \"partner\": \"No\",\n    \"dependents\": \"No\",\n    \"internet_service\": \"Fiber optic\",\n    \"paperless_billing\": \"Yes\",\n    \"payment_method\": \"Electronic check\",\n    \"services_list\": [\"Multiple Lines\",\"Streaming TV\"]\n  }'\n```\n\n### 4) Run the Shiny App\n\n```r\n# from frontend/\nsetwd(\"frontend\")\nshiny::runApp(\"app.R\", launch.browser = TRUE)\n```\n\n---\n\n## API\n\n**Base**: `http://127.0.0.1:8000`\n\n### `GET /health`\n\n* **200** → `{ \"status\": \"ok\" }`\n\n### `POST /predict`\n\n**Body (JSON):**\n\n```json\n{\n  \"tenure\": 12,\n  \"monthly_charges\": 70,\n  \"contract\": \"Month-to-month\",\n  \"gender\": \"Female\",\n  \"senior_citizen\": 0,\n  \"partner\": \"Yes\",\n  \"dependents\": \"No\",\n  \"internet_service\": \"DSL\",\n  \"paperless_billing\": \"Yes\",\n  \"payment_method\": \"Electronic check\",\n  \"services_list\": [\"Streaming TV\", \"Online Security\"]\n}\n```\n\n**Response (JSON):**\n\n```json\n{\n  \"prediction\": \"No\",\n  \"probability\": 0.1673\n}\n```\n\n\u003e The API auto-derives engineered fields (e.g., total/avg charges, tenure buckets, service flags) to match the training pipeline.\n\n---\n\n## Usage Notes \u0026 Tips\n\n* **Filters**: Multi-select dropdowns accept empty selection = “All”. Tenure range slider updates the dataset reactively.\n* **Dark theme**: Custom CSS fixes for `selectize` ensure selected chips remain visible.\n* **Model threshold**: Training step chooses a probability threshold that maximizes F1 on the test split; the API uses 0.50 by default (you can expose the tuned threshold if desired).\n\n---\n\n## Troubleshooting\n\n* **“Could not resolve host: backend” in Shiny prediction**\n  Use the full URL `http://127.0.0.1:8000/predict` in `prediction_server(..., api_url = ...)` when running locally. Use `http://backend:8000/predict` only inside docker-compose networks.\n\n* **Swagger “Invalid JSON body”**\n  Click **Try it out** → paste a valid JSON object in the request body (don’t post empty).\n\n* **“Could not derive probability from model prediction”**\n  Ensure the trained workflow is saved as `model.rds` and predictions support `type=\"prob\"`. This repo uses tidymodels (`.pred_1`) and includes a robust `get_positive_proba()` in `main.R`.\n\n* **Connection refused on port 5432**\n  Make sure Postgres is running and credentials/host (`localhost` vs `postgres`) match how you launched the DB.\n\n---\n\n## Roadmap\n\n* Add segmented lift/ICE plots for top features.\n* Expose tuned threshold from training into API response.\n* Optional authentication for the API.\n* Dockerized one-click stack (compose: db + API + Shiny).\n\n---\n\n## Contributing\n\nPRs and issues are welcome. Keep styles consistent with the repo (e.g., R assignments with `=`). If you add a feature in the UI, please include a short GIF and update this README.\n\n---\n\n## License\n\nSee **`LICENSE`** in the repository root.\n\n---\n\n### Built With\n\n* R, Shiny, bslib (`darkly`)\n* plumber\n* tidymodels (recipes, workflows, tune, yardstick), xgboost\n* dplyr, ggplot2, plotly, DT\n* PostgreSQL\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fash-datapro%2Fchurn-pred","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fash-datapro%2Fchurn-pred","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fash-datapro%2Fchurn-pred/lists"}