К основному содержанию
скилл CX Codex GP ChatGPT официальный · OpenAI

mixpanelyst — скилл для Codex: аналитика и установка

Скилл помогает запрашивать и разбирать данные Mixpanel через Python. Пригодится для воронок, удержания, сегментов и поведения пользователей.

Установить в Codex · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add openai/plugins --skill mixpanelyst -a codex
Открыть на GitHub

Что делает mixpanelyst

mixpanelyst работает с библиотекой mixpanel_headless и pandas: запускает Python-код для запросов к Mixpanel и возвращает результаты в виде pandas DataFrame. Доступны анализ событий и трендов, воронки, удержание, пользовательские данные и пути пользователей. Для flow-анализа результат также содержит граф и дерево путей.

Перед любым запросом нужно изучить схему событий и свойства, а затем проверить методы через help.py. Это снижает риск пустых результатов из-за выдуманного имени события или ошибки в параметре. Для быстрых проверок используются однострочные команды python3 -c, многошаговый анализ сохраняется в .py-файлы. Перед запуском вспомогательных скриптов задайте SKILL_DIR как абсолютный путь к каталогу скилла.

Какие задачи решает

  • найти реальные события Mixpanel и их свойства
  • посчитать тренды и метрики по событиям
  • построить воронку конверсии
  • проверить удержание и когорты
  • разобрать пути пользователей

Примеры запросов агенту

  • Проверь события Mixpanel и покажи свойства самых активных из них.
  • Построй воронку регистрации и первой покупки, используя реальные имена событий.
  • Разбери пути пользователей после события Login и представь результат таблицей.

Как установить mixpanelyst

1Через Skills CLI (Vercel)

npx skills add openai/plugins --skill mixpanelyst -a codex

2Вручную: положить файлы

.agents/skills/mixpanelyst/SKILL.md

Для всех проектов — ~/.agents/skills/

По шагам, с командами для каждой платформы: Скиллы Codex: как установить, где лежат и как вызвать

Вопросы о mixpanelyst

Как установить mixpanelyst в Codex?

Выполните npx skills add openai/plugins --skill mixpanelyst -a codex, затем проверьте файл .agents/skills/mixpanelyst/SKILL.md.

Что делает mixpanelyst?

Скилл анализирует данные Mixpanel через mixpanel_headless и pandas: запрашивает события, строит воронки и удержание, изучает пользователей и их пути.

Почему скилл сначала изучает события и API?

Потому что неверное имя события даёт пустой результат, а ошибочный параметр приводит к TypeError. Для проверки методов используется help.py.

Исходный текст

SKILL.md · MIT
---
name: mixpanelyst
description: This skill should be used when the user asks about Mixpanel product analytics, event data, funnel analysis, retention curves, cohort analysis, segmentation queries, user behavior, conversion rates, churn, DAU/MAU, ARPU, revenue metrics, feature adoption, A/B test results, user paths, flow analysis, or any request to query, explore, visualize, or analyze Mixpanel data using Python. Also use when the user asks to read, write, or manage Mixpanel "business context" — the markdown documentation that grounds AI assistants in an organization's structure and goals.
allowed-tools: Bash Read Write WebFetch
---

# mixpanel_headless API Reference

Analyze Mixpanel data by writing and executing Python code using the `mixpanel_headless` library and `pandas`.
Before running bundled helper scripts, set `SKILL_DIR` to the absolute path of this
`skills/mixpanelyst` directory.

```python
import mixpanel_headless as mp
ws = mp.Workspace()
result = ws.query("Login", last=30)
print(result.df.head())
```

## Query Engines

| Question | Method | Returns |
|----------|--------|---------|
| How much? How many? Trends? | `ws.query()` | `QueryResult` |
| Do users convert through a sequence? | `ws.query_funnel()` | `FunnelQueryResult` |
| Do users come back? | `ws.query_retention()` | `RetentionQueryResult` |
| What paths do users take? | `ws.query_flow()` | `FlowQueryResult` |
| Who are they? What do they look like? | `ws.query_user()` | `UserQueryResult` |

All result types have a `.df` property returning a pandas DataFrame and a `.params` dict containing the bookmark JSON.
`FlowQueryResult` also has `.graph` (NetworkX DiGraph) and `.anytree` (list of tree roots).

**Quick lookups** use `python3 -c "..."` one-liners. **Multi-step analysis** writes `.py` files.

## Discovery — ALWAYS Do Both Steps Before Querying

Guessing event names causes silent empty results. Guessing API parameters causes TypeErrors and invalid queries. **Discover both the data schema AND the API surface before writing any query.**

### Step 1: Discover the data schema

```python
import mixpanel_headless as mp
from mixpanel_headless import Filter, GroupBy, Metric
ws = mp.Workspace()

# 1. Find real event names
events = ws.events()
top = ws.top_events(limit=10)
print("Events:", events[:20])
print("Top:", [(e.event, e.count) for e in top])

# 2. Find real property names for the event you'll query
props = ws.properties("Login")  # use an actual event name from step 1
print("Properties:", props)

# 3. (Optional) Check property values to validate filter inputs
vals = ws.property_values("platform", event="Login")
print("Platforms:", vals)
```

### Step 2: Discover the API surface with `help.py`

**`help.py` is the source of truth for method signatures, parameter names, type constructors, and enum values.** The method signatures later in this document are summaries — always verify with `help.py` before using a method or type you haven't looked up.

**Never guess parameter names.** If you're unsure whether a parameter is called `property` or `math_property`, or what arguments `GroupBy()` accepts, run `help.py` first. Wrong parameter names cause TypeErrors that waste tool calls.

```bash
# Look up a query method BEFORE writing the query
python3 $SKILL_DIR/scripts/help.py Workspace.query
python3 $SKILL_DIR/scripts/help.py Workspace.query_funnel

# Look up types BEFORE constructing them
python3 $SKILL_DIR/scripts/help.py Filter          # → classmethods: .equals(), .less_than(), etc.
python3 $SKILL_DIR/scripts/help.py Metric          # → property=, NOT math_property=
python3 $SKILL_DIR/scripts/help.py GroupBy          # → property, property_type only
python3 $SKILL_DIR/scripts/help.py MathType         # → enum values

# Look up result types to know what columns .df returns
python3 $SKILL_DIR/scripts/help.py QueryResult
python3 $SKILL_DIR/scripts/help.py FlowQueryResult

# Search when you're not sure of the exact name
python3 $SKILL_DIR/scripts/help.py search cohort   # → CohortBreakdown, CohortMetric, CohortDefinition, ...
python3 $SKILL_DIR/scripts/help.py search retention # → query_retention, RetentionEvent, RetentionMathType, ...

# List everything
python3 $SKILL_DIR/scripts/help.py types            # all public types
python3 $SKILL_DIR/scripts/help.py exceptions        # all exceptions
```

For tutorials and guides: `WebFetch(url="https://mixpanel.github.io/mixpanel-headless/llms.txt")`

### Discovery method signatures

```python
def events(self) -> list[str]: ...
    # List all event names (cached).

def properties(self, event: str) -> list[str]: ...
    # List all property names for an event (cached).

def property_values(self, property_name: str, *, event: str | None = None, limit: int = 100) -> list[str]: ...
    # Get sample values for a property.

def top_events(self, *, type: Literal['general', 'average', 'unique'] = 'general', limit: int | None = None) -> list[TopEvent]: ...
    # Get today's most active events. TopEvent has .event (str), .count (int), .percent_change (float).

def funnels(self) -> list[FunnelInfo]: ...
    # List saved funnels.

def cohorts(self) -> list[SavedCohort]: ...
    # List saved cohorts.

def list_bookmarks(self, bookmark_type: BookmarkType | None = None) -> list[BookmarkInfo]: ...
    # List all saved reports (bookmarks).

def lexicon_schemas(self, *, entity_type: EntityType | None = None) -> list[LexiconSchema]: ...
    # List Lexicon schemas (event/property definitions).

def clear_discovery_cache(self) -> None: ...
    # Clear cached discovery results.
# User Guide: WebFetch(url="https://mixpanel.github.io/mixpanel-headless/guide/discovery/index.md")
```

## Exploratory Analysis Workflow

When exploring an unfamiliar dataset or asked to "find insights," follow this systematic approach. Do NOT skip to querying — explore first.

### Step 1: Orient — Map the Event Schema

```python
import mixpanel_headless as mp
ws = mp.Workspace()

events = ws.events()
top = ws.top_events(limit=15)
print("Events:", events)
print("Top events:", [(e.event, e.count) for e in top])

# Profile the top 3-5 events by volume
for event in [e.event for e in top[:5]]:
    props = ws.properties(event)
    print(f"\n{event} ({len(props)} properties):")
    for p in props[:15]:
        vals = ws.property_values(p, event=event, limit=10)
        print(f"  {p}: {vals}")
```

### Step 2: Classify Properties

Infer property types from sampled values to decide how to use each:

- **Boolean**: values are `['true', 'false']` — segment with `group_by`, often pre-computed behavioral flags
- **Low-cardinality categorical** (<10 values): `platform`, `tier`, `category` — use for `group_by`
- **Numeric**: values parse as int/float: `price`, `total`, `count` — use for `math='average'` or `math='sum'` with `math_property=`
- **High-cardinality** (>100 values): IDs, names — skip for `group_by`, may need custom property cleanup
- **Temporal**: ISO dates or epoch values — use for time-based analysis

Property naming patterns that signal analytical value:
- `is_*`, `has_*`, `was_*`, `post_*` → boolean flags, often pre-computed behavioral segments worth investigating
- `*_total`, `*_count`, `*_value`, `*_amount` → numeric, aggregate with avg/sum/median
- `*_name`, `*_type`, `*_category`, `*_tier` → categorical, use for breakdowns
- `*_id`, `*_uuid` → identifiers, skip for breakdowns

### Step 3: Scan for Significant Segments

For each boolean and low-cardinality categorical property on key events, run a quick breakdown against a numeric metric:

```python
# Example: scan all interesting properties on a purchase event
numeric_prop = 'order_total'  # or whatever the key metric is
interesting_props = [p for p in props if not p.endswith('_id')]

for prop in interesting_props:
    vals = ws.property_values(prop, event=event, limit=10)
    if len(set(vals)) <= 10:  # low cardinality — worth a breakdown
        result = ws.query(event, math='average', math_property=numeric_prop,
                           group_by=prop, last=90, mode='total')
        print(f"\n{numeric_prop} by {prop}:")
        print(result.df.to_string(index=False))
        # Flag segments where metric differs >15% from overall
```

### Step 4: Deep Dive on Significant Findings

When a breakdown reveals a notable difference (>15% between segments):
1. **Quantify**: calculate the exact ratio between segments
2. **Cross-reference**: does this segment differ on other metrics too?
3. **Investigate causally**: run funnels or retention filtered by the segment
4. **Control for confounds**: add a second `group_by` dimension to check if the effect holds

### Step 5: Analyze Messy String Properties

When string properties have complex/unreadable values (e.g., campaign names from tools like Braze):
1. Sample 15-20 values to identify the naming convention
2. Look for structural patterns: date codes, targeting prefixes, channel suffixes, audience tags
3. Design regex cleanup rules, one layer per structural element
4. Create a custom property with `ws.create_custom_property(CreateCustomPropertyParams(...))`
5. Verify by querying with the custom property as `group_by`

### Custom Property Formula Reference

Formulas use a SQL-like expression language. Variables (A, B, _A, etc.) map to properties via `composedProperties`.

**Variable binding:** `LET(name, expression, body)` — define intermediate results:
```
LET(raw, A, REGEX_REPLACE(raw, "pattern", "replacement"))
LET(x, A * B, IFS(x < 50, "low", x < 200, "mid", TRUE, "high"))
```

**Conditionals:** `IF(cond, then, else)`, `IFS(cond1, val1, cond2, val2, ..., TRUE, default)`

**String functions:** `UPPER(s)`, `LOWER(s)`, `LEN(s)`, `LEFT(s, n)`, `RIGHT(s, n)`, `MID(s, start, count)`, `SPLIT(s, delim, n)`, `HAS_PREFIX(s, p)`, `HAS_SUFFIX(s, p)`, `PARSE_URL(s, "domain")`

**Regex functions (PCRE2 engine):**
- `REGEX_MATCH(haystack, pattern)` — returns true/false
- `REGEX_EXTRACT(haystack, pattern, capture_group)` — returns match or capture group
- `REGEX_REPLACE(haystack, pattern, replacement)` — replaces all matches

**Regex engine quirks (Mixpanel-specific):**
- **Case-insensitive by default** — use `(?-i)` to switch to case-sensitive matching within a pattern
- **Backreferences work** — `$1`, `$2` capture groups and `$0` whole-match all work in `REGEX_REPLACE` replacements
- **`{n,m}` quantifiers conflict with formula syntax** — curly braces are parsed as formula constructs. Use repeated character classes instead (e.g., `[0-9][0-9][0-9][0-9]` instead of `[0-9]{4}`)
- **`\d`, `\w` shorthand classes don't work** — use `[0-9]`, `[A-Za-z0-9_]` explicitly
- **Escape backslashes carefully** — in formula strings, `\\\\` may be needed for a literal `\` depending on how the formula is constructed (Python string → JSON → regex engine)

**CamelCase splitting** — insert space between lowercase→uppercase boundaries:
```
REGEX_REPLACE(text, "(?-i)([a-z])([A-Z])", "$1 $2")
// ChickenSundaysApril → Chicken Sundays April
```

**Practical multi-step cleanup example** (campaign names from Braze):
```
LET(s1, REGEX_REPLACE(A, "^[0-9][0-9][0-9][0-9][0-9]*_", ""),
LET(s2, REGEX_REPLACE(s1, "^(NW|TARGETED|REGIONAL|NTL)_", ""),
LET(s3, REGEX_REPLACE(s2, "_(Push|Email|NotificationCenter|ModalInAppMessage)_.*$", ""),
LET(s4, REGEX_REPLACE(s3, "_", " "),
  REGEX_REPLACE(s4, " +", " ")
))))
```

**Type functions:** `STRING(x)`, `NUMBER(x)`, `BOOLEAN(x)`, `DEFINED(x)`

**Math:** `+`, `-`, `*`, `/`, `%`, `MIN(a,b)`, `MAX(a,b)`, `FLOOR(n)`, `CEIL(n)`, `ROUND(n)`

**Date:** `DATEDIF(start, end, unit)` — units: D, M, Y, MD, YM, YD. `TODAY()` for current date.

**List:** `SUM(list)`, `ANY(x, list, expr)`, `ALL(x, list, expr)`, `FILTER(x, list, expr)`, `MAP(x, list, expr)`

**Comparison:** `==`, `!=`, `<`, `>`, `<=`, `>=` (case-insensitive for strings), `IN` for list membership

**Logical:** `AND`, `OR`, `NOT(x)`

**Constants:** `TRUE`, `FALSE`, `UNDEFINED`

**Creating a custom property via the API:**
```python
from mixpanel_headless import CreateCustomPropertyParams, ComposedPropertyValue

params = CreateCustomPropertyParams(
    name="Clean Campaign Name",
    resource_type="events",
    display_formula='LET(raw, A, REGEX_REPLACE(REGEX_REPLACE(raw, "^[0-9]+_", ""), "_", " "))',
    composed_properties={
        "A": ComposedPropertyValue(
            resource_type="event", type="string", value="campaign_name",
            label="Campaign Name", property_default_type="string",
        )
    }
)
prop = ws.create_custom_property(params)
ref = CustomPropertyRef(prop.custom_property_id)
result = ws.query(event, group_by=GroupBy(ref), last=30, mode='total')
```

## Workspace

```python
class Workspace:
    """Unified entry point for Mixpanel data operations (042 redesign)."""

    def __init__(
        self,
        *,
        account: str | None = None,
        project: str | None = None,
        workspace: int | None = None,
        target: str | None = None,
        session: Session | None = None,
    ) -> None:
        """Create a new Workspace. Resolution per axis is independent
        (env > param > target > bridge > config); see
        ``mixpanel_headless.auth_types`` and the resolver.

        With ``session=`` supplied, all other axis kwargs are ignored
        (full bypass).
        """
        ...

    # --- Properties (read-only) ---
    account: Account            # Resolved Account (discriminated union)
    project: Project            # Resolved Project
    workspace: WorkspaceRef | None  # Resolved workspace, or None for lazy-resolve
    session: Session            # The (account, project, workspace) tuple
    api: MixpanelAPIClient      # Direct API client access (escape hatch)

    def use(
        self,
        *,
        account: str | None = None,
        project: str | None = None,
        workspace: int | None = None,
        target: str | None = None,
        persist: bool = False,
    ) -> Self:
        """Switch any axis in-session. Returns self for chaining.
        Preserves the underlying httpx.Client. With persist=True, also
        writes to ~/.mp/config.toml [active]. ``target=`` is mutex
        with the per-axis kwargs.
        """
        ...
```

Supports context manager: `with mp.Workspace() as ws: ...`

### Project & Workspace Management

```python
def me(self, *, force_refresh: bool = False) -> Any: ...
    # Get /me response for current credentials (cached 24h).

def projects(self) -> list[Project]: ...
    # List accessible projects (v3; returns Project records — id, name,
    # organization_id, timezone). Replaces deprecated discover_projects().

def workspaces(self, *, project_id: str | None = None) -> list[WorkspaceRef]: ...
    # List workspaces in a project (v3; returns WorkspaceRef records —
    # id, name, is_default). Replaces deprecated discover_workspaces().

def list_workspaces(self) -> list[PublicWorkspace]: ...
    # List all public workspaces for the current project (App API).

def resolve_workspace_id(self) -> int: ...
    # Auto-discover and resolve workspace ID (lazy-resolve helper).

def close(self) -> None: ...
    # Close all resources (HTTP client). Idempotent.
```

> **Removed (042 redesign — FR-038):** `Workspace.workspace_id` property,
> `set_workspace_id()`, `switch_project()`, `switch_workspace()`,
> `discover_projects()`, `discover_workspaces()`, `current_project`,
> `current_credential`, `test_credentials()`. Use `ws.session.workspace_id`,
> `ws.use(workspace=N)`, `ws.use(project=P)`, `ws.projects()`,
> `ws.workspaces()`, `ws.project`, `ws.account`, and `mp.accounts.test(NAME)`
> respectively.

### Insights Query

Run `python3 $SKILL_DIR/scripts/help.py Workspace.query` for the full signature.

```python
def query(
    self,
    events: str | Metric | CohortMetric | Formula | Sequence[...],
    *,
    from_date: str | None = None,        # YYYY-MM-DD, overrides last
    to_date: str | None = None,          # YYYY-MM-DD, requires from_date
    last: int = 30,                      # relative days (ignored if from_date set)
    unit: QueryTimeUnit = 'day',
    math: MathType = 'total',            # aggregation: total, unique, dau, average, sum, ...
    math_property: str | None = None,    # top-level shorthand; Metric() uses property= instead
    per_user: PerUserAggregation | None = None,
    percentile_value: int | float | None = None,
    group_by: str | GroupBy | CohortBreakdown | FrequencyBreakdown | list[...] | None = None,
    where: Filter | FrequencyFilter | list[...] | None = None,
    formula: str | None = None,          # e.g. "(B / A) * 100", requires 2+ events
    formula_label: str | None = None,
    rolling: int | None = None,
    cumulative: bool = False,
    mode: Literal['timeseries', 'total', 'table'] = 'timeseries',
    time_comparison: TimeComparison | None = None,
    data_group_id: int | None = None,
) -> QueryResult:
    # .df columns: timeseries → [date, event, count]
    #              total → [event, count]
    #              with group_by → adds segment column
    ...
```

### Funnel Query

Run `python3 $SKILL_DIR/scripts/help.py Workspace.query_funnel` for the full signature.

```python
def query_funnel(
    self,
    steps: list[str | FunnelStep],      # at least 2 steps required
    *,
    conversion_window: int = 14,
    conversion_window_unit: Literal['second', 'minute', 'hour', 'day', 'week', 'month', 'session'] = 'day',
    order: Literal['loose', 'any'] = 'loose',
    from_date: str | None = None, to_date: str | None = None, last: int = 30,
    unit: QueryTimeUnit = 'day',
    math: FunnelMathType = 'conversion_rate_unique',
    math_property: str | None = None,
    group_by: str | GroupBy | CohortBreakdown | list[...] | None = None,
    where: Filter | list[Filter] | None = None,
    exclusions: list[str | Exclusion] | None = None,
    holding_constant: str | HoldingConstant | list[...] | None = None,
    mode: Literal['steps', 'trends', 'table'] = 'steps',
    reentry_mode: FunnelReentryMode | None = None,
    time_comparison: TimeComparison | None = None,
    data_group_id: int | None = None,
) -> FunnelQueryResult:
    # .df columns: [step, event, count, step_conv_ratio, avg_time]
    # .overall_conversion_rate: float
    ...
```

### Retention Query

Run `python3 $SKILL_DIR/scripts/help.py Workspace.query_retention` for the full signature.

```python
def query_retention(
    self,
    born_event: str | RetentionEvent,
    return_event: str | RetentionEvent,
    *,
    retention_unit: TimeUnit = 'week',
    alignment: RetentionAlignment = 'birth',
    bucket_sizes: list[int] | None = None,
    from_date: str | None = None, to_date: str | None = None, last: int = 30,
    unit: QueryTimeUnit = 'day',
    math: RetentionMathType = 'retention_rate',
    group_by: str | GroupBy | CohortBreakdown | list[...] | None = None,
    where: Filter | list[Filter] | None = None,
    mode: RetentionMode = 'curve',
    unbounded_mode: RetentionUnboundedMode | None = None,
    retention_cumulative: bool = False,
    time_comparison: TimeComparison | None = None,
    data_group_id: int | None = None,
) -> RetentionQueryResult:
    # .df columns: [cohort_date, bucket, count, rate]  (+ segment with group_by)
    # .average: synthetic average across cohorts
    ...
```

### Flow Query

Run `python3 $SKILL_DIR/scripts/help.py Workspace.query_flow` for the full signature.

```python
def query_flow(
    self,
    event: str | FlowStep | Sequence[str | FlowStep],
    *,
    forward: int = 3, reverse: int = 0,
    from_date: str | None = None, to_date: str | None = None, last: int = 30,
    conversion_window: int = 7,
    conversion_window_unit: Literal['day', 'week', 'month', 'session'] = 'day',
    count_type: Literal['unique', 'total', 'session'] = 'unique',
    cardinality: int = 3,
    collapse_repeated: bool = False,
    hidden_events: list[str] | None = None,
    mode: Literal['sankey', 'paths', 'tree'] = 'sankey',
    where: Filter | list[Filter] | None = None,
    segments: str | GroupBy | CohortBreakdown | FrequencyBreakdown | list[...] | None = None,
    exclusions: list[str] | None = None,
    data_group_id: int | None = None,
) -> FlowQueryResult:
    # .df, .graph (NetworkX DiGraph), .anytree (tree mode)
    # .top_transitions(n), .drop_off_summary()
    ...
```

### User Profile Query

Run `python3 $SKILL_DIR/scripts/help.py Workspace.query_user` for the full signature.

```python
def query_user(
    self,
    *,
    where: Filter | list[Filter] | str | None = None,
    cohort: int | CohortDefinition | None = None,
    properties: list[str] | None = None,
    sort_by: str | None = None,
    sort_order: Literal['ascending', 'descending'] = 'descending',
    limit: int | None = 1,              # None = fetch all matching
    search: str | None = None,
    distinct_id: str | None = None,     # single user lookup
    distinct_ids: list[str] | None = None,  # batch lookup
    group_id: str | None = None,        # query group profiles
    as_of: str | int | None = None,     # point-in-time
    mode: Literal['profiles', 'aggregate'] = 'aggregate',
    aggregate: Literal['count', 'extremes', 'percentile', 'numeric_summary'] = 'count',
    aggregate_property: str | None = None,
    percentile: float | None = None,
    segment_by: list[int] | None = None,
    parallel: bool = False, workers: int = 5,
    include_all_users: bool = False,
) -> UserQueryResult:
    # .df, .total, .profiles
    ...
```

### Build Params (without executing)

Same parameters as the corresponding query methods, but return `dict[str, Any]` bookmark params without making an API call. Useful for creating saved reports (bookmarks).

```python
def build_params(self, events, **kwargs) -> dict[str, Any]: ...
def build_funnel_params(self, steps, **kwargs) -> dict[str, Any]: ...
def build_retention_params(self, born_event, return_event, **kwargs) -> dict[str, Any]: ...
def build_flow_params(self, event, **kwargs) -> dict[str, Any]: ...
def build_user_params(self, **kwargs) -> dict[str, Any]: ...
```

### Multi-Step Analysis Patterns

Every query engine has parameters that look like simple settings but are actually analytical choices with outsized influence on results. Before running any query, apply these principles:

- [ ] **Find the master dial.** Each engine has one parameter (or small set) that reshapes all downstream metrics. Changing it changes the story. Know which parameter it is and choose deliberately — don't accept defaults blindly.
- [ ] **Match parameters to the domain.** There are no universal "correct" values. A social app needs daily retention; a B2B SaaS needs monthly. A food-ordering funnel needs a 1-hour window; an onboarding funnel needs 14 days. The product's natural usage cadence dictates the setting.
- [ ] **Distrust averages.** Averages include outliers — one extreme value distorts the whole metric. Use medians (`math='median'`, `percentile=50`) to see what's typical. If the mean and median diverge, the distribution is skewed and the mean is misleading.
- [ ] **Counting methodology is a modeling choice.** "Unique users," "total events," and "sessions" aren't just modes — they answer fundamentally different questions. "How many people?" vs "How much activity?" vs "How many engagement moments?" Choose the counting method that matches the business question.
- [ ] **Know the silent defaults.** Parameters are sometimes silently ignored (e.g., `math_property` with `math='unique'`), silently constraining (e.g., no funnel re-entry by default), or silently inflating (e.g., `unbounded_mode='carry_forward'` in retention). If results look surprising, check whether a default is shaping them.
- [ ] **Sweep, don't guess.** When unsure which parameter value to use, try several and observe how metrics shift. Where the metric stabilizes or diverges reveals the true signal. The code examples below demonstrate this for each engine.

#### Comparing Segments Across Multiple Dimensions

When a single breakdown shows a difference, verify it holds across dimensions:

```python
# Step 1: Find the interesting segment
result = ws.query(event, math='average', math_property='order_total',
                   group_by='deal_sweet_spot', last=90, mode='total')
# Found: deal_sweet_spot=true has 37% higher AOV

# Step 2: Check if it holds across another dimension
result = ws.query(event, math='average', math_property='order_total',
                   group_by=['deal_sweet_spot', 'platform'], last=90, mode='total')
# Does the sweet spot hold for both iOS and Android?

# Step 3: Check segment rates across a third dimension
result = ws.query(event, math='unique',
                   group_by=['loyalty_tier', 'deal_sweet_spot'], last=90, mode='total')
# Which tier is most likely to achieve the sweet spot?
```

#### Insights Analysis: MathType, Per-User, and the Unit of Analysis

**MathType is the most critical Insights parameter.** It defines what you're counting — `total` (event volume), `unique` (user reach), `dau/wau/mau` (time-bounded engagement), `average/median/percentile` (property distributions), `sum` (revenue totals). Choosing the wrong MathType answers the wrong question silently. Match MathType to the business question: engagement → `dau` or `wau`; revenue → `sum` or `average` with `math_property`; adoption → `unique`; intensity → `total`.

**Per-user aggregation is a two-stage process** that fundamentally changes the unit of analysis. `per_user='average'` with `math='average'` first computes each user's average, then averages across users. This is NOT the same as a global average — a power user with 1000 events and a casual user with 2 events contribute equally. This is often the right choice (prevents power users from dominating) but it changes the story dramatically:

```python
# Sweep MathTypes to understand an event from multiple angles
event = 'Purchase'  # use a real event name
prop = 'order_total'  # use a real numeric property

for mt in ['total', 'unique', 'dau', 'average', 'median', 'sum']:
    kwargs = {'math': mt, 'last': 30, 'mode': 'total'}
    if mt in ('average', 'median', 'sum'):
        kwargs['math_property'] = prop
    result = ws.query(event, **kwargs)
    print(f"{mt:>10}: {result.df['count'].iloc[0]:>12,.2f}")
# total = event volume, unique = user reach, dau = daily engagement,
# average/median = typical transaction, sum = total revenue

# Per-user aggregation: compare global average vs per-user average
global_avg = ws.query(event, math='average', math_property=prop, last=30, mode='total')
per_user_avg = ws.query(event, math='average', math_property=prop,
                         per_user='average', last=30, mode='total')
print(f"Global avg: {global_avg.df['count'].iloc[0]:.2f}")
print(f"Per-user avg: {per_user_avg.df['count'].iloc[0]:.2f}")
# If these differ significantly, power users are skewing the global average
```

**Prefer medians over averages for property distributions** — same principle as funnel time-to-convert. Use `math='median'` instead of `math='average'`, or `math='percentile'` with `percentile_value=50`. Averages include outliers; medians reveal what's typical.

**Silent traps:** `math_property` is silently ignored with non-property MathType (e.g., `math='unique'` discards `math_property`). `rolling` reduces data point count without warning (a 30-day rolling window over 59 days produces ~29 points, not 59). `unit` is silently ignored in `mode='total'`.

#### Funnel Analysis: Windows, Modes, and Time

Funnel queries return time data in step metadata columns (`avg_time`, `avg_time_from_start`) alongside conversion rates.

**Conversion window is the most critical funnel parameter.** It defines the maximum time a user has to complete the funnel from their first step. It affects every other metric — conversion rate, time-to-convert, and segment comparisons all shift dramatically with window size.

**Choosing a window:** Match it to the user journey being measured. Short funnels (ordering food, adding to cart) need tight windows — hours, not days. Long funnels (onboarding, subscription purchase) need wider windows — days or weeks. When unsure, experiment:

```python
# Try progressively tighter windows to find where signal emerges
for window, unit in [(14, 'day'), (7, 'day'), (1, 'day'), (12, 'hour'), (6, 'hour')]:
    result = ws.query_funnel(steps, last=90,
        conversion_window=window, conversion_window_unit=unit)
    final = result.df[result.df['step'] == result.df['step'].max()].iloc[0]
    print(f"{window}{unit}: conv={final['overall_conv_ratio']:.3f} "
          f"time={final['avg_time_from_start']/3600:.1f}h")
# Look for: conversion stabilizing, time differences appearing at tighter windows
```

**Conversion counting modes** change what "conversion" means:
- `conversion_rate_unique` (default): unique users who completed. No re-entry — first attempt in the window or out.
- `conversion_rate_total`: total completions. One user can count multiple times.
- Combine with `reentry_mode='basic'` or `reentry_mode='optimized'` for multiple attempts. Optimized re-entry picks the best completion path.

**Time-to-convert: prefer medians over averages.** Average time includes outliers — one slow user inflates the average; one fast user pulls it down. Use median or percentiles for true speed trends:

```python
# Median time via funnel math
result = ws.query_funnel(steps, last=90, math='median')

# Compare segments with filtered funnels + tight window
ios = ws.query_funnel(steps, where=Filter.equals('platform', 'iOS'),
    last=90, conversion_window=6, conversion_window_unit='hour')
android = ws.query_funnel(steps, where=Filter.equals('platform', 'Android'),
    last=90, conversion_window=6, conversion_window_unit='hour')
# Compare avg_time_from_start on matching steps
```

**When comparing segments across funnels:** always try at least 2-3 conversion windows. A difference invisible at 14 days may be stark at 6 hours. This is especially true for speed comparisons — tighter windows filter out noise and reveal which segment c...

© Mixpanel · MIT · openai/plugins

Похожие скиллы

Все скиллы по теме «Браузер и парсинг» →
agent-browser vercel-labs Скилл управляет браузером через CLI: открывает страницы, заполняет формы, нажимает кнопки и проверяет веб-приложения. Подходит для задач, где агенту нужно действовать в интерфейсе. CX Codex +1 1 200 000установок webapp-testing официальный anthropics Скилл запускает проверки локальных веб-приложений через Playwright: помогает отлаживать UI, делать скриншоты и смотреть логи браузера. 186 758установок computer-use stablyai Скилл ведёт видимое окно приложения через orca computer: читает дерево доступности, кликает, печатает и снимает скриншоты. Нужен, когда задача упирается в интерфейс. CX Codex +1 239 600установок browser-use browser-use Даёт агенту живой Chrome через CDP: клики, ввод, скриншоты и страницы, которым нужен JS или ваша логин-сессия. CX Codex +1 98 200установок playwright-cli microsoft Скилл, который даёт агенту браузер: открыть страницу, кликнуть по элементу, снять снимок и разобрать тесты Playwright. CX Codex +1 195 700установок last30days mvanhorn Собирает посты и реакции по любой теме за последние 30 дней из Reddit, X, YouTube, TikTok, Hacker News, Polymarket, GitHub и веба. Пригодится, когда нужно понять, что реально говорят люди, а не что пишут в обзорах. CX Codex +1 53 000установок

Из того же набора528

Весь набор →
omniverse-cad-to-simready Координирует путь от CAD-модели или другого исходного ассета до SimReady USD и пакета. Нужен для конвертации, назначения материалов и физики, проверки и подготовки ассета. CX Codex GP ChatGPT ★ 7 604звёзд репо dynamo-interconnect-check Проверяет, что транспорт NIXL/UCX/NCCL в развёртывании Dynamo действительно работает. Нужен перед тем, как доверять цифрам disaggregated-обслуживания. CX Codex GP ChatGPT ★ 7 604звёзд репо cuopt-user-rules Скилл задаёт агенту правила, как помогать с NVIDIA cuOpt: маршрутизация, LP, MILP, QP, установка и сервер. CX Codex GP ChatGPT ★ 7 604звёзд репо aiq-deploy Скилл разворачивает и проверяет инфраструктуру NVIDIA AI-Q Blueprint, а также помогает остановить сервисы и разобрать сбои. CX Codex GP ChatGPT ★ 7 604звёзд репо mixpanel-auth Скилл для Codex, который ведёт учётные записи Mixpanel: проверяет сессию, добавляет аккаунты, запускает OAuth и переключает проекты. CX Codex GP ChatGPT ★ 7 604звёзд репо magicpath Скилл подключает агента к платформе MagicPath: ищет компоненты на канвасе, ставит их в проект и правит кодом. Пригодится, если дизайн живёт в MagicPath, а приложение — в репозитории. CX Codex GP ChatGPT ★ 7 604звёзд репо review-design-parity Сравнивает реализованный интерфейс с макетом Figma и перечисляет расхождения по вёрстке, токенам и поведению. Пригодится перед сдачей экрана. CX Codex GP ChatGPT ★ 7 604звёзд репо implement-from-figma Команда переносит фреймы и компоненты из макетов Figma прямо в код проекта. Она скачивает графику и сверяет вёрстку с дизайном. CX Codex GP ChatGPT ★ 7 604звёзд репо

Спонсоры проекта

Компании, которые помогают держать аналитику открытой