Analyzing Funnel Correlation logo

Analyzing Funnel Correlation

Organization
PostHog
analyzing-funnel-correlation

Find the events and person properties that correlate with funnel conversion or drop-off. Use when the user asks why people convert or drop off in a funnel, what converters do differently, which events or properties predict success or failure, or asks for "correlation analysis" on a funnel. Computes odds ratios with HogQL, because PostHog has no built-in funnel correlation view.

Overview

PublisherPostHog
Repositoryskills
Skill nameanalyzing-funnel-correlation
Stars
77
Forks
8
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by PostHog on GitHub. Read the source before you install it.

Installation

Install the Analyzing Funnel Correlation AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/PostHog/skills/tree/main/skills/omnibus/analyzing-funnel-correlation
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/PostHog/skills.git /tmp/skills
mkdir -p .claude/skills
cp -r /tmp/skills/skills/omnibus/analyzing-funnel-correlation .claude/skills/analyzing-funnel-correlation
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Analyzing Funnel Correlation in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Analyzing Funnel Correlation on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Analyzing Funnel Correlation is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Analyzing funnel correlation

Correlation compares two groups of people who entered a funnel: people who completed the last step (success) and people who did not (failure). For each candidate event or property value, build a contingency table and compute the odds ratio. An odds ratio above 1 means the signal goes with conversion. Below 1 means it goes with drop-off. The further the ratio is from 1, the stronger the correlation.

Correlation is not causation. Tell the user that, and suggest an experiment to test a strong signal.

Workflow

  1. Get the funnel definition: the step events, the date range, and the conversion window. If the user names a saved insight, read it with insight-get. Confirm the step events with read-data-schema.
  2. Run the funnel with query-funnel and note the total entrants and converters. The SQL below must give totals in the same range. If not, fix the step filters first.
  3. Run the event correlation query below with execute-sql.
  4. If the user asks about person attributes, run the property variant.
  5. Report the top success signals and the top failure signals with their counts. To show who is behind a signal, use query-funnel-actors or filter the funnel by that event or property.

Event correlation

Replace the step events, the date range, the conversion window (seconds in windowFunnel, days in the INTERVAL), and the step count. Add more event = '...' conditions to windowFunnel for more steps, and set steps = N to the last step.

sql
WITH
    funnel_actors AS (
        SELECT
            person_id AS actor_id,
            windowFunnel(1209600)(toDateTime(timestamp), event = 'step one', event = 'step two') AS steps,
            arraySort(groupArrayIf((toUnixTimestamp(timestamp), event = 'step two'), event IN ('step one', 'step two'))) AS step_events,
            arrayReverseFill(t -> t > 0, arrayMap(x -> if(x.2, x.1, 0), step_events)) AS next_final,
            arrayFirstIndex((x, t) -> NOT x.2 AND t > 0 AND t <= x.1 + 1209600, step_events, next_final) AS attempt,
            toDateTime(if(attempt > 0, step_events[attempt].1, arrayFirst(x -> NOT x.2, step_events).1)) AS first_timestamp,
            toDateTime(next_final[greatest(attempt, 1)]) AS final_timestamp
        FROM events
        WHERE event IN ('step one', 'step two')
            AND timestamp >= now() - INTERVAL 30 DAY
        GROUP BY actor_id
        HAVING steps >= 1
    ),
    totals AS (
        SELECT countIf(steps = 2) AS success_total, countIf(steps < 2) AS failure_total
        FROM funnel_actors
    ),
    per_event AS (
        SELECT
            e.event AS name,
            countDistinctIf(a.actor_id, a.steps = 2) AS success_count,
            countDistinctIf(a.actor_id, a.steps < 2) AS failure_count
        FROM events AS e
        JOIN funnel_actors AS a ON e.person_id = a.actor_id
        WHERE e.timestamp >= now() - INTERVAL 30 DAY
            AND e.timestamp > a.first_timestamp
            AND e.timestamp < if(a.steps = 2, a.final_timestamp, a.first_timestamp + INTERVAL 14 DAY)
            AND e.event NOT IN ('step one', 'step two')
        GROUP BY name
    )
SELECT
    name,
    success_count,
    failure_count,
    success_total,
    failure_total,
    ((success_count + 1) * (failure_total - failure_count + 1))
        / ((success_total - success_count + 1) * (failure_count + 1)) AS odds_ratio
FROM per_event
CROSS JOIN totals
WHERE success_count + failure_count >= least(25, 0.02 * (success_total + failure_total))
ORDER BY abs(log(odds_ratio)) DESC
LIMIT 20

The query only counts events between funnel entry and conversion. step_events holds the first-step and last-step events in time order. next_final gives the next last-step time for each of them in one pass. For people who converted, attempt picks the first entry that has a last step inside the window, so both timestamps come from the same attempt. For more steps, keep only the first and last step events in groupArrayIf. The attempt then ignores the middle steps, so the interval can start at an earlier entry than the one that converted. For people who did not convert, it counts events from their first entry until the conversion window closes. The timestamps use whole seconds because windowFunnel compares whole seconds. Keep them the same, or some converters get no matched attempt. It excludes the funnel step events, because they correlate with conversion by definition.

Property correlation

To compare person property values, group the actors by the property and skip the events join.

sql
WITH
    funnel_actors AS (
        SELECT
            person_id AS actor_id,
            windowFunnel(1209600)(toDateTime(timestamp), event = 'step one', event = 'step two') AS steps,
            any(person.properties.$browser) AS value
        FROM events
        WHERE event IN ('step one', 'step two')
            AND timestamp >= now() - INTERVAL 30 DAY
        GROUP BY actor_id
        HAVING steps >= 1
    ),
    totals AS (
        SELECT countIf(steps = 2) AS success_total, countIf(steps < 2) AS failure_total
        FROM funnel_actors
    ),
    per_value AS (
        SELECT
            toString(value) AS name,
            countIf(steps = 2) AS success_count,
            countIf(steps < 2) AS failure_count
        FROM funnel_actors
        GROUP BY name
    )
SELECT
    name,
    success_count,
    failure_count,
    ((success_count + 1) * (failure_total - failure_count + 1))
        / ((success_total - success_count + 1) * (failure_count + 1)) AS odds_ratio
FROM per_value
CROSS JOIN totals
WHERE success_count + failure_count >= least(25, 0.02 * (success_total + failure_total))
ORDER BY abs(log(odds_ratio)) DESC
LIMIT 20

To look at the properties of one event instead, filter per_event to that event and group by e.properties.<name>.

Read the results with care

  • The + 1 in each cell is a prior. It prevents division by zero and damps signals from small counts.
  • The WHERE filter drops signals seen by fewer than 25 people or 2% of entrants. Small groups give unstable ratios.
  • If one group is more than 10 times bigger than the other, the ratios are less reliable. Tell the user.
  • If success_total or failure_total is 0, there is nothing to compare.
  • Noise events that everybody sends, such as $pageview, $pageleave, or $feature_flag_called, can fill the list. Exclude them when they hide the useful signals.
  • For group-based funnels, replace person_id with $group_N and the person properties with group properties. Also add AND $group_N != '' to the funnel step WHERE clause. Events without that group have an empty key, and without the filter they become one fake actor that can convert.

Frequently asked questions

What does the Analyzing Funnel Correlation AI skill do?

Find the events and person properties that correlate with funnel conversion or drop-off. Use when the user asks why people convert or drop off in a funnel, what converters do differently, which events or properties predict success or failure, or asks for "correlation analysis" on a funnel. Computes odds ratios with HogQL, because PostHog has no built-in funnel correlation view.

Why use Analyzing Funnel Correlation on TypingMind?

Because you install it once and use it with any model. Analyzing Funnel Correlation is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Analyzing Funnel Correlation in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/PostHog/skills/tree/main/skills/omnibus/analyzing-funnel-correlation. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Analyzing Funnel Correlation?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Analyzing Funnel Correlation?

As many as you like. As long as a model supports skills, you can use Analyzing Funnel Correlation with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Analyzing Funnel Correlation AI skill free?

Yes. It is published on GitHub by PostHog under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇