# Data schema

## Superset schema

All walk data follows this schema. Internally, `node_id` and `exit_edge` are stored as **BIGINT** (64-bit integers) for fast DuckDB joins and compact Parquet files. If you supply string IDs, they are auto-translated (see [String IDs](#string-nodeedge-ids) below).

| Column | Type | Required | Description |
|--------|------|----------|-------------|
| `walk_id` | String/Int | Yes | Unique identifier for the walk |
| `sequence_idx` | Integer | Yes | Step order (0, 1, 2, ...) |
| `node_id` | BIGINT (Int64) | Yes | Node visited at this step |
| `exit_edge` | BIGINT (Int64) | No | Edge taken to next step |
| `timestamp` | Timestamp | No | Time when node was reached |
| `dwell_time` | Interval | No | Duration spent at node |
| `dynamic_attributes` | JSON/MAP | No | Walk/step-specific metadata |

NULL values cost practically nothing in Parquet, so optional columns don't waste storage.

## String node/edge IDs

Many real-world datasets use human-readable identifiers -- station names, page URLs, warehouse codes, ticker symbols. walkingpandas accepts them transparently:

```python
import walkingpandas as wp
import pandas as pd

network = wp.Network()
network.add_nodes(pd.DataFrame({"node_id": ["Station_A", "Station_B", "Station_C"]}))
network.add_edges(pd.DataFrame({
    "edge_id": ["link_AB", "link_BC"],
    "source_node": ["Station_A", "Station_B"],
    "target_node": ["Station_B", "Station_C"],
}))
print(network.has_string_mapping)  # True
```

Under the hood, each unique string gets a dense integer. A mapping table (`wp_node_map` / `wp_edge_map`) is stored in DuckDB so the translation survives save/load.

### Getting data back

`get_nodes()`, `get_edges()`, and `compute()` all accept a `decode` parameter (default `True`). When decoding is on, results show your original string labels; set `decode=False` to see the raw integers.

```python
network.get_nodes(decode=True)   # node_id: "Station_A", "Station_B", ...
network.get_nodes(decode=False)  # node_id: 0, 1, 2

walks_df = pd.DataFrame({
    "walk_id": ["w1", "w1"],
    "sequence_idx": [0, 1],
    "node_id": ["Station_A", "Station_B"],
    "exit_edge": ["link_AB", None],
})
wf = wp.WalkFrame.from_dataframe(walks_df, network=network)

wf.compute(decode=True)   # node_id/exit_edge show original strings
wf.compute(decode=False)  # raw integers
```

### Filtering with strings

Queries like `filter(node_id="Station_A")` or `passing_through(["Station_A", "Station_B"])` resolve the strings to integers internally before hitting DuckDB.
