# Frame operations Ordinary `CognitionFrame` transformations are immutable and lazy. Call `collect()`, `collect_records()`, `to_records()`, an export method, or `pack()` to execute the plan. In the 0.1 series, `group_by().agg()` evaluates its source immediately. ```python active = ( frame .filter(cx.col("expires_at").is_null() | (cx.col("expires_at") > cx.now())) .with_columns(priority=lambda row: row["priority"] + 1) .sort("priority", descending=True) .limit(100) ) print(active.explain()) ``` ## Operation summary | Operation | 0.1-series behavior | | --- | --- | | `filter(predicate)` | Keeps rows for which an `Expr` or callable is truthy | | `select(*columns)` | Projects columns; missing names become `None` | | `with_columns(**values)` | Adds or replaces values from constants, expressions, or callables | | `sort(by, descending=False)` | Sorts one column with null values grouped by the Python planner | | `limit(count)` | Keeps the first non-negative number of rows | | `join(other, on=..., how=...)` | Supports `inner` and `left`; colliding right names get a suffix | | `group_by(*columns).agg(...)` | Supports `count`, `sum`, `min`, `max`, and `list` | | `dedupe()` | Exact canonical `content_hash` matching | | `rank(query, ranker=None)` | Stores a score without changing row order | | `redact(pattern, replacement)` | Regex substitution for Text payloads only, with rehashing and lineage | | `quarantine(predicate)` | Changes matching rows to `Trust.QUARANTINED` | | `demote(predicate, to=...)` | Explicitly changes retention; this is an application authorization decision | ## Context frames and exploratory tables `collect()` converts every row back to `ContextItem` and therefore requires `id` and `payload`. `collect_records()` returns ordinary dictionaries and can inspect projected or aggregated rows. ```{testcode} import cognoxium as cx frame = cx.CognitionFrame.from_records([ {"id": "a", "payload": "one", "metadata": {"team": "x"}}, {"id": "b", "payload": "two", "metadata": {"team": "x"}}, ]).with_columns(team=lambda row: row["metadata"]["team"]) summary = frame.group_by("team").agg(count=("count", "*")) print(summary.collect_records()) ``` ```{testoutput} [{'team': 'x', 'count': 2}] ``` Calling `summary.collect()` or `summary.pack()` raises `SchemaError` because an aggregate table is not model context.