Viewing as: HR Switch: Manager Colleague

Product Analyst — for hiring

Role, stack and metrics — to match against a vacancy.

Analysis

SQL Python pandas scikit-learn DuckDB visualization

Visualization

Plotly Tableau Superset Astro

Experiments

A/B AA-tests Causal Inference

Retention and segmentation

cohort analysis retention curves RFM

Infra

GitHub Actions Docker Airflow Supabase PostHog
All projects →

All projects

  • Situation
    'Volta' is a fictional neobank losing users during onboarding: every team had its own number, and none explained where the money leaked
    Task
    Find the critical onboarding step and prove it so the conclusion cannot be dismissed as a convenient sample
    Action
    One discover → validate → measure → optimize loop: funnel, KYC progress-bar A/B, cohorts, KMeans segmentation, DiD causality check
    Result
    KYC fix: +5.72pp conversion (p<0.0001) → €208.5K over 12 months at 13.9× ROI; M3 retention +9.2pp

    Python · pandas / NumPy · SciPy / Statsmodels · scikit-learn

  • Situation
    An A/B method is only as good as its Type I error under the null and its power under a real effect
    Task
    Make the method guarantees verifiable, backed by a simulation run rather than by a citation
    Action
    Modules from the primary literature — SRM, CUPED, delta method, mSPRT, HTE, BH — each calibrated by simulation
    Result
    CUPED cuts variance 17.7% (SE 0.00360 → 0.00327) and the delta method finds a +0.51 pp CTR lift at p < 0.0001 with SRM p = 0.8376 — all 18 modules calibrated by simulation

    Python · NumPy / SciPy · pytest · uv

  • Situation
    Analytics concepts are abstract: a recruiter or student can't feel them from text
    Task
    Make a metric's meaning obvious from the action itself, not from an explanation
    Action
    Self-contained SVG games: A/B test, funnel drop, cohort catch, retention day, SQL and metric match
    Result
    10 games: 7 analytics and 3 arcade, one file with no server; Playwright smoke tests catch load failures

    SVG · JavaScript · Astro · Playwright

  • Situation
    Analytics portfolios show metrics on a clean CSV; the hard part is analytics embedded in a real multi-tenant product
    Task
    Own the multi-tenant setup end to end: auth, per-org isolation, event ingest, and the experiment result computed in the database
    Action
    SQL views compute funnel, cohort, MRR, and DAU; v_results computes A/B in the DB; RLS isolates rows per organization
    Result
    A/B concluded on a seeded stream: control 29.6% vs treatment 40.7%, +11.1pp at p ≈ 1.7e-10 (chi-square); metrics computed in the live DB for any client

    Python · Streamlit · Supabase (Postgres + RLS + Edge Functions) · SQL

  • Situation
    An analytics portfolio usually starts from a ready-made CSV; this one starts earlier — with instrumenting the app
    Task
    Define events without PII leaks, get them into the analytics tool, and turn raw events into decisions
    Action
    Typed event catalog, PII scrubbing; A/B with chi-square, uplift, Wilson CI, SRM; SQL mirror, Streamlit dashboard, CI, Docker
    Result
    Onboarding test on a seeded stream: completion 55.9% → 70.8% (+26.6%, χ² = 40.10, p < 0.0001, SRM p = 0.90), and 6.3% of completed signups reach a paid upgrade

    Python · PostHog · FastAPI · Streamlit

  • Situation
    The portfolio shows the whole AARRR cycle on one dataset of 8,000 synthetic users, not one metric in isolation
    Task
    Build the metrics so they read as one coherent story rather than a set of disconnected charts
    Action
    Deterministic seed and shared cache: one base for Funnel, Retention, Revenue, and Segments; 5 Streamlit pages
    Result
    Funnel, Retention, Revenue, and Segments read from one consistent base; the same UI became the Supabase project's presentation layer

    Python · Streamlit · pandas / NumPy

  • Situation
    An AI call flow is a 4-step funnel over 16,891 synthetic calls; the export does not say which step loses the client
    Task
    Take call breakdown off manual review and name the funnel step that loses the most clients
    Action
    Step labels from templated script markers, leak broken down by type, weighted contact loss, engagement by hour
    Result
    An answer in a minute: which step loses the client and what to fix; the funnel yields weighted contact loss and a first A/B scenario

    Python · Streamlit · pandas

  • Situation
    No production data, and textbook exercises do not show systems thinking: 25 cases on a synthetic dataset
    Task
    Build cases where every query answers a product question and reproduces with one command
    Action
    One self-contained .sql per case, data generator on DuckDB, dbt staging → marts layer, regression tests, live report on Pages
    Result
    The funnel drops 78% at add-to-cart → checkout (54% is the view→cart step); retention falls from ~19.5% (D1) to ~4% (D30); only 3.5% repeat; on real data 72.4% return

    SQL · dbt · DuckDB · Python

  • Situation
    ClickHouse cases were checked by eye: the result snapshot was not bound to the SQL files, and the optimisation win mixed a rewritten query with a schema change
    Task
    I needed a lab where every case is scored against a snapshot and an independent engine, and the optimisation win is measured with the query text held constant
    Action
    50M synthetic events in MergeTree, ten cases from windowFunnel to RFM, DuckDB twins for seven of them, a projection on product_id, and a test asserting the slow and fast SQL are byte-identical
    Result
    The snapshot and seven DuckDB twins agree, slow and fast SQL are pinned as identical by a test, and the projection clears the ≥10× speed-up threshold the suite asserts on a full scan

    ClickHouse · DuckDB · SQL · Python

  • Situation
    Delivery is measured as one average time, while the real question — where time is lost and whether capacity matches the forecast — needs a split by category, channel and city
    Task
    Build a reproducible loop: ETL, marts, order forecast, bottleneck analysis, testable hypotheses and a SQL layer with measured optimizations
    Action
    DuckDB star schema, rolling-origin backtest of four models, time decomposition and load curve, Mann-Whitney with bootstrap CIs, 14 window queries and 3 optimization pairs under EXPLAIN ANALYZE
    Result
    Best WAPE 6.01% at h=7 with seasonal_naive; delivery time barely moves with load (rho = −0.092), yet snacks take 40.0 min against 26.6 min for pizza; the mp channel share grew 5.7 pp

    python · sql · duckdb · superset

  • Situation
    A 'GMV dropped 10%' alert does not say where the money went: the same total is equally consistent with a collapse in one segment and with a broad decline
    Task
    I needed to decompose the drop down to a concrete segment and prove the attribution was complete rather than a convenient slice
    Action
    Metric tree GMV = users × orders_per_user × AOV on the named population, country × decile decomposition, a separate guest addend, Shapley contributions, an executive dashboard
    Result
    UK decile-1 explains 56.2% of the named drop (−£195,218.25); the full attribution reconciles to £0.00 against an overall −10.0% decline

    Python · pandas · DuckDB · Plotly

  • Situation
    The platform never sees the sale: it closes off-platform, so liquidity is counted from the seller's self-report rather than from an actual transaction
    Task
    I needed to measure how far the proxy drifts from the planted truth per category, and to test whether a paid-visibility decision survives that correction
    Action
    A synthetic log of 6,000 listings and 40,000 sessions with a planted answer key, an EM sold/unsold mixture over observable reply signals, a per-category correction, and a randomised holdout with guardrails
    Result
    The proxy reads 0.2413 against a truth of 0.3070, an error of −0.0657 (−21.4%); after correction the worst category misses by 1.61 pp, and paid visibility yields −2.47% contacts per session at p = 0.308

    Python · pandas · DuckDB · SciPy

  • Situation
    The bank accumulates client transactions, but marketing sends everyone the same offer
    Task
    Replace the single offer with targeted segments, each with its own retention strategy
    Action
    Cleaned the transactions, scored Recency, Frequency and Monetary, segmented clients, visualized the distribution in Tableau
    Result
    Six named segments; the top 20% of clients hold 38.2% of revenue (Gini 0.324), and Champions — 6.4% of the base — drive 12.1% of revenue

    Python · SQL · Tableau

  • Situation
    Cohort retention and LTV on synthetic data: average retention hides the per-cohort dynamics
    Task
    Make retention and LTV readable per acquisition cohort rather than as a single average
    Action
    Retention matrix, ARPU and LTV by cohort, observation-age caveat, Tableau export (CSV + Hyper)
    Result
    Python inside Tableau: 4 cohort calculations on a TabPy server and 65 tests; the cohort matrix replaces average retention and shows churn speed and where monetization diverges

    Python · pandas · matplotlib / seaborn · Jupyter Notebook

  • Situation
    A churn model for a subscription product: a random split leaks future activity into training
    Task
    Score the model so its offline quality is not an artifact of leakage
    Action
    As-of features, a future inactivity label, a chronological split, decisions driven by recall@top-decile and lift
    Result
    The top 10% riskiest catches 54.4% of churners at 5.44x lift, but only 1.75x on the at-risk subgroup: 69.7% of the cohort is a near-guaranteed negative by construction.

    Python · LightGBM · scikit-learn · pandas / NumPy

  • Situation
    A two-sample t-test ignores the pre-period: experiments ask for more traffic than needed and retention offers go to everyone
    Task
    Implement CUPED and uplift modeling and prove neither is biased where the true answer is known in advance
    Action
    CUPED on a pre-period covariate and T-/S-learners on LightGBM; checked against ground truth with AUUC and QINI
    Result
    CUPED cuts the standard error 26% (5.5k users per arm instead of 10k), and a pre-registered break-even threshold of 0.05 ships the campaign to new users only.

    Python · LightGBM · scikit-learn · pandas / NumPy

  • Situation
    Product A/B habits ported into CRM break randomization: users are bucketed at first exposure instead of when the campaign segment is built
    Task
    I needed a lab where every CRM experiment stage is checked against data with a known answer and can be made to fail visibly
    Action
    Simulator over 20,000 users and 12 campaigns: segment-stage randomization, a global control group, a send balancer, and the net effect of unsubscribes
    Result
    Segment-stage randomization holds SRM at p=0.266, GCG uplift is +0.9968 [0.9433, 1.0502], and the net effect is +50.5 per 1,000 treated — positive, but the unsubscribe guardrail breaches.

    Python · pandas / NumPy · SciPy · statsmodels

  • Situation
    A RAG demo is easy to show and hard to measure: you cannot tell whether the retrieval or the generation is failing, or whether the result reproduces at all
    Task
    Build a pipeline with an offline eval harness that separates retrieval from generation metrics and runs without API keys
    Action
    A TF-IDF index over the concept corpus, cosine top-k, a deterministic extractive answerer; Claude plugs in behind a --llm flag and the harness stays the same
    Result
    recall@5 and MRR = 1.00 over 20 questions and 0.00 on an 11-question control set of retrieval misses (MRR@all 0.12) — the metric can fall; correctness 0.37 for the offline baseline (faithfulness ≡1.00 is an extractive invariant); retrieval and generation scored separately

    Python · scikit-learn · pandas / NumPy · TF-IDF

  • Situation
    In product analytics most tasks start the same way: load an export, clean it, check distributions, chart it — from scratch each time
    Task
    Turn the routine into shared modules that teammates can extend without fear of breaking existing behavior
    Action
    load / clean / EDA / viz / pipeline modules mapped to PRD requirements; tests in tests/ check those requirements; uv + ruff tooling
    Result
    pytest coverage ≥80%; modules stay small and single-purpose, requirements live in the PRD, and the tool extends without breakage

    Python · pandas / NumPy · Matplotlib · pytest

  • Situation
    PMs and analysts often have no BI access: checking an A/B hypothesis waits for a dashboard, and the exported CSV sits there without statistics
    Task
    Check a hypothesis right in Telegram — get a correct result with guardrails from a CSV, without BI and without a notebook
    Action
    Bot on aiogram, pandas, scipy and statsmodels: 12 commands, Welch t-test or chi-square, SRM and power guardrails, analytics as pure modules, pytest + ruff + mypy in CI
    Result
    From a CSV — p-value, lift and 95% CI in under 30 seconds, plus a power note and a chart; /export returns a standalone HTML report

    Python · aiogram · pandas · SciPy / statsmodels

  • Situation
    Workplace English (A2–B1) stalls for many Russian-speaking specialists: the grammar is there, coherent speech is not
    Task
    Close the gap between grammar and live speech: give learners situations where phrases are used, not recognized
    Action
    Single ScrollyLayout, MDX narrative sections, typed data module for viz props, IntersectionObserver with lazy imports
    Result
    Narrative and visualization in sync: the chart changes as the reader reaches a paragraph; a new scene is just a data file plus MDX

    Astro · TypeScript · MDX · D3

  • Situation
    Chronological posts age and lose connections; a digital garden links notes with wikilinks instead
    Task
    Publish a personal Zettelkasten as a static site where the links between notes are visible to the reader
    Action
    Atomic notes in Obsidian-flavored Markdown; Quartz: TypeScript plugins, backlinks, graph view, static output
    Result
    Links over chronology: backlinks make each note a node, and the graph turns note accumulation into a navigable structure

    TypeScript · Quartz v4 · Markdown · Obsidian

  • SERP Rank Lab

    Analytics

    Situation
    A metasearch product earns on the position an offer occupies, so ranking and monetisation are not measured by the same number
    Task
    Find out how much revenue ranking for money produces, and what that revenue is paid for in clicks
    Action
    Two feasible policies on one information set, a user_id rollout with guardrails and a stopping rule, then a diagnosis through the signal source
    Result
    On a synthetic log: +34.45% revenue per search for −16.84% CTR; the rollout returns −14.26% (p = 0.11) and −16.05% conversion → do not ship

    Python · DuckDB · Streamlit · NumPy / SciPy

  • Situation
    A hand-run report from Postgres into Google Sheets produced delays, copy errors and no reproducibility at all
    Task
    Replace the manual export with a reproducible, idempotent pipeline with DQ validation, failure alerts and run metrics
    Action
    A platform on Airflow 3: the DAG template fetch → validate → write → record_metrics and the shared modules config, dq, alerts, metrics, output, windows
    Result
    Three DAGs on one shared template and 72 tests without Airflow services or a DB; the full DAG runs in 440 s, 413 s of it in the forecast task

    Python · Apache Airflow 3 · PostgreSQL · Docker Compose

  • Situation
    Preprocessing is the foundation of all later ML work: a test leak into training or hand-rolled imputation invalidates every model above it
    Task
    Build preprocessing correctly for modern scikit-learn: no leakage, no reliance on a random seed, reproducible at every pipeline stage
    Action
    ColumnTransformer (SimpleImputer → StandardScaler for numerics, OneHotEncoder for categoricals) fit on train only; a deterministic split by sorting on (Country, Age); a dataset generator and EDA plots
    Result
    A split reproducible without a seed (Train (8, 5), Test (2, 5)), four synthetic datasets and 21 tests (7 + 9 + 5) that keep the pipeline runnable

    Python · scikit-learn · pandas · matplotlib

  • Situation
    A format ranking whose confidence interval covers the whole field is not an answer: showing it as one is worse than showing nothing
    Task
    Answer not 'how many impressions did I get' but 'which format to write next — and is the difference even established'
    Action
    One workbook-parsing core, four renderers (markdown report, HTML dashboard, SVG chart, vault note) and per-post bootstrap intervals instead of a point ranking
    Result
    50,101 impressions, 9,440 reach, 1.71% ER over 12.10.2025 — 11.10.2026; three of four comparisons landed inside the noise, and that is recorded as the result

    Python · openpyxl · uv · bootstrap

  • Situation
    Offline retail measures promo against a pre-period average and picks the discount depth without pricing cannibalisation or margin
    Task
    Measure promo in margin roubles and choose depth and store set under a budget so that the numbers can be re-checked
    Action
    Synthetic panel with known truth, counterfactual, four-bucket ledger, P&L, breakeven, cluster test validated by simulation, Qini against policy value, budget allocation
    Result
    Promo returns +68.5 m RUB of revenue and −24.4 m RUB of profit; 97.9% of waves lose money at a breakeven depth of 1.0–19.5% against 25.4% actual, and on one budget the optimiser earns +231 496 ₽ versus +136 648 ₽ for top-15%

    Python · pandas · scikit-learn · PySpark

  • Situation
    A subscription B2C service must decide whether to roll out a new onboarding flow and cannot say which lever drives more revenue
    Task
    Tie the experiment result to the funnel, cohort retention and unit economics, then issue one management verdict
    Action
    A/B with SRM, ITT and power checks, a funnel down to payment, cohorts and LTV, and a conversion-versus-retention comparison at an equal step
    Result
    SHIP: activation → paid +19.59% (p=0.000946) with no guardrail degradation; at an equal step conversion adds +10.00% to LTV versus +8.65% for retention

    Python · pandas / NumPy · DuckDB · SciPy

  • Situation
    The project catalog was maintained by hand: cards drifted from GitHub, and metadata and links had to be updated in two places
    Task
    I needed a catalog where the data comes from one machine-owned source, pages are built from it, and a repeated sync does not overwrite authored edits
    Action
    Astro 7 with content collections, a Python sync from the GitHub API into TOML, a frontmatter generator with an ownership marker, a notes validator and 26 tests, and CI ordered from the cheap gate to the build
    Result
    47 project pages and 15 metadata fields are built from one TOML file; a repeated render is idempotent, and 26 tests cover all three record-matching branches

    Astro · TypeScript · Bun · Python

  • Situation
    A conference programme page is a Vue SPA: the data lives in an API and the layout in a bundle, so 'make it the same' usually means 'make it look similar'
    Task
    Take a third-party interface apart down to measurable quantities and rebuild it with no build step and no backend
    Action
    Pulled the programme from the public API, measured the grid on the rendered DOM, rebuilt it in plain HTML/CSS/JS, then ran four independent passes: code review, security audit, browser tests, visual audit
    Result
    The grid matches the original to the pixel: 99 px time column, 420 px hall column, 391 px card, 550 px row, grid top at y = 414; tests catch data and layout drift

    HTML · CSS · JavaScript · Python

  • Situation
    A static portfolio on GitHub Pages: projects and posts are edited in Markdown, not in component markup
    Task
    Make publishing a project a content change, not a code change: a new Markdown file, then a push
    Action
    Content collections with Zod schemas, withBase() for the base path, no-flash inline theme script, SEO set, push-to-Pages deploy
    Result
    Invalid frontmatter breaks the build, not the deploy; no link hardcodes the base; adding a project needs no code changes

    Astro · TypeScript · Markdown · CSS custom properties

Send the vacancy

I'll match the requirements against the cases and say plainly where the experience is backed by numbers and where it needs an interview.

Open contacts →