Pixelex.Query.Funnel (Pixelex v0.1.0)

Copy Markdown View Source

How many people made it from the first step to the last, and where the rest stopped.

Nothing else on Hex does this. Plausible Community Edition withholds funnels as a paid feature, so in Elixir today the only way to answer this question is to ship your data to PostHog.

Pixelex.Query.Funnel.run("shop", range, [
  "px.pageview", "view_doctor", "book_click", "booking_completed"
])

%{
  steps: [
    %{name: "px.pageview",        count: 4182, rate: 1.0,    step_rate: 1.0,   dropped: 3011},
    %{name: "view_doctor",        count: 1171, rate: 0.28,   step_rate: 0.28,  dropped: 894},
    %{name: "book_click",         count: 277,  rate: 0.066,  step_rate: 0.237, dropped: 129},
    %{name: "booking_completed",  count: 148,  rate: 0.035,  step_rate: 0.534, dropped: 0}
  ],
  conversion_rate: 0.035,
  subject: :visitor
}

rate is against the first step; step_rate is against the step before — the second is what tells you which step is broken.

Order is enforced, not assumed

Each step is built from the one before it in its own CTE, so step 3 only counts events that happened at or after that subject's step 2. The shortcut most implementations take — min(occurred_at) FILTER (WHERE name = step_n) in a single pass — silently drops anyone who did step 3 before step 2 and again after it, and silently counts nobody whose steps interleave. This costs one CTE per step and is correct.

Which subject: :visitor or :user

visitor_id is a hash under a salt that rotates every UTC day, so a funnel keyed on it cannot span midnight. That is right for a checkout and wrong for an onboarding funnel measured over a week.

run(site, range, steps, subject: :user)

keys on user_id instead, which only exists after Pixelex.identify/3. Pick :visitor for a single-visit funnel and :user for anything longer, and be aware that :user counts only signed-in people.

Conversion window

run(site, range, steps, within: {2, "hours"})

bounds how long a subject has to get from one step to the next. Without it, a visitor who bounced in March and converted in May counts as one funnel.

Summary

Types

subject()

@type subject() :: :visitor | :user

Functions

run(site_id, range, steps, opts \\ [])

@spec run(String.t(), Pixelex.Query.Range.t(), [String.t()], keyword()) :: map()

Run an ordered funnel.

Options

  • :subject:visitor (default) or :user
  • :within{amount, unit}, e.g. {30, "minutes"}. Units are minutes, hours or days.