> ## Documentation Index
> Fetch the complete documentation index at: https://tif1.tracinginsights.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Data Backends

> Understanding pandas vs polars backends in tif1

tif1 supports two DataFrame backends: pandas (default) and polars (optional). Each has different performance characteristics and use cases.

## Lib Comparison

````mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
graph LR
    subgraph "Pandas"
        P1[Single-threaded]
        P2[Mature ecosystem]
        P3[Wide compatibility]
    end

    subgraph "Polars"
        PL1[Multi-threaded]
        PL2[2x faster]
        PL3[50% less memory]
    end

    style P2 fill:#51cf66
    style P3 fill:#51cf66
    style PL1 fill:#51cf66
    style PL2 fill:#51cf66
    style PL3 fill:#51cf66
```python

## Pandas Lib

The default lib, providing maximum compatibility with the Python data science ecosystem.

### When to Use Pandas

- You're already familiar with pandas
- You need compatibility with existing pandas code
- You're using libraries that require pandas DataFrames
- You're working with small to medium datasets (< 1M rows)
- You need the full pandas API surface

### Example

```python
import tif1

# Pandas is the default
session = tif1.get_session(2025, "Monaco Grand Prix", "Race")
laps = session.laps  # Returns pandas DataFrame

# Standard pandas operations
fastest = laps.nsmallest(5, "LapTime")
by_driver = laps.groupby("Driver")["LapTime"].mean()
```python

---

## Polars Lib

A high-performance alternative written in Rust, offering 2-5x faster operations and 50% less memory usage.

### When to Use Polars

- You're processing large datasets (> 1M rows)
- You need maximum performance
- You want to minimize memory usage
- You're building data pipelines
- You're comfortable with a slightly different API

### Installation

```bash
pip install tifone
```python

### Example

```python
import tif1

# Specify polars lib
session = tif1.get_session(
    2025,
    "Monaco Grand Prix",
    "Race",
    lib="polars"
)
laps = session.laps  # Returns polars DataFrame

# Polars operations (similar but not identical to pandas)
fastest = laps.sort("LapTime").head(5)
by_driver = laps.group_by("Driver").agg(pl.col("LapTime").mean())
```python

---

## Performance Comparison

| Operation | Pandas | Polars | Speedup |
| :--- | :--- | :--- | :--- |
| Load 20 drivers | 2.5s | 1.2s | **2.1x** |
| Filter laps | 45ms | 12ms | **3.8x** |
| Group by driver | 120ms | 35ms | **3.4x** |
| Sort by time | 80ms | 25ms | **3.2x** |
| Memory usage | 450MB | 220MB | **2.0x** |

<Note>
  Benchmarks based on a full race session with ~1200 laps and telemetry data.
</Note>

---

## API Differences

While tif1 abstracts most differences, there are some API variations to be aware of.

### Filtering

<CodeGroup>
```python Pandas
# Boolean indexing
fast_laps = laps[laps["LapTime"] < 90]

# Query method
fast_laps = laps.query("LapTime < 90")
```python

```python Polars
# Filter method
fast_laps = laps.filter(pl.col("LapTime") < 90)

# Also supports boolean indexing
fast_laps = laps[laps["LapTime"] < 90]
```python
</CodeGroup>

### Grouping

<CodeGroup>
```python Pandas
# GroupBy
by_driver = laps.groupby("Driver").agg({
    "LapTime": ["mean", "min", "count"]
})
```python

```python Polars
# group_by with agg
import polars as pl

by_driver = laps.group_by("Driver").agg([
    pl.col("LapTime").mean().alias("mean_time"),
    pl.col("LapTime").min().alias("min_time"),
    pl.col("LapTime").count().alias("count")
])
```python </CodeGroup>

### Sorting

<CodeGroup>
```python Pandas
# sort_values
sorted_laps = laps.sort_values("LapTime")
```python

```python Polars
# sort
sorted_laps = laps.sort("LapTime")
```python
</CodeGroup>

---

## Converting between backends

You can convert DataFrames between backends as needed.

```python
# Start with polars
session = tif1.get_session(2025, "Monaco", "Race", lib="polars")
laps_polars = session.laps

# Convert to pandas for compatibility
laps_pandas = laps_polars.to_pandas()

# Convert back to polars
import polars as pl
laps_polars_again = pl.from_pandas(laps_pandas)
```yaml

---

## Setting default lib

### Via Configuration File

Create `~/.tif1rc`:

```json
{
  "lib": "polars"
}
```python

### Via Environment Variable

```bash
export TIF1_LIB=polars
```python

### Programmatically

```python
import tif1

config = tif1.get_config()
config.set("lib", "polars")
config.save()
```python

---

## Lib-Specific Features

### Pandas Advantages

- Richer ecosystem (seaborn, plotly, etc.)
- More Stack Overflow answers
- Better Jupyter display
- Time series functionality
- More flexible indexing

### Polars Advantages

- Lazy evaluation (query optimization)
- Better parallelization
- Lower memory footprint
- Faster string operations
- Native Arrow support

---

## Lazy Evaluation (Polars Only)

Polars supports lazy evaluation for query optimization.

```python
import polars as pl

# Create lazy frame
laps_lazy = pl.scan_parquet("race_data.parquet")

# Build query (not executed yet)
result = (
    laps_lazy
    .filter(pl.col("Driver") == "VER")
    .select(["LapNumber", "LapTime", "Compound"])
    .sort("LapTime")
)

# Execute query (optimized)
result_df = result.collect()
```python

<Tip>
  Lazy evaluation allows polars to optimize the entire query plan before execution, often resulting in significant speedups.
</Tip>

---

## Recommendations

<Steps>
  <Step title="Start with Pandas">
    If you're new to tif1 or data analysis, start with pandas for familiarity.
  </Step>
  <Step title="Profile Your Code">
    If performance becomes an issue, profile to identify bottlenecks.
  </Step>
  <Step title="Try Polars">
    Switch to polars for large datasets or performance-critical code.
  </Step>
  <Step title="Mix as Needed">
    You can use different backends for different sessions in the same script.
  </Step>
</Steps>

---

## Common Patterns

### Processing multiple sessions

```python
# Use polars for heavy lifting
results = []
for event in tif1.get_events(2024):
    session = tif1.get_session(2024, event, "Race", lib="polars")
    fastest = session.get_fastest_laps(by_driver=True)
    results.append(fastest)

# Combine and convert to pandas for plotting
import polars as pl
all_results = pl.concat(results)
all_results_pd = all_results.to_pandas()

# Plot with pandas/matplotlib
import matplotlib.pyplot as plt
all_results_pd.plot(x="Driver", y="LapTime", kind="bar")
```python ### Memory-Efficient Analysis

```python
# Use polars to minimize memory usage
session = tif1.get_session(2024, "Abu Dhabi", "Race", lib="polars")

# Process in chunks
for driver in session.drivers:
    driver_obj = session.get_driver(driver)
    laps = driver_obj.laps

    # Analyze
    avg_time = laps["LapTime"].mean()
    print(f"{driver}: {avg_time:.3f}s")

    # Data is automatically freed after loop iteration
```yaml

---

## Summary

- **Pandas**: Default, familiar, compatible
- **Polars**: Fast, efficient, modern
- **Choice**: Depends on your needs and dataset size
- **Flexibility**: You can use both in the same project

---

## Related Pages

<CardGroup cols={2}>
  <Card title="Common Use Cases" href="/guides/common-use-cases">
    Performance guide
  </Card>
  <Card title="Performance" href="/guides/best-practices">
    Optimization
  </Card>
  <Card title="Installation" href="/installation">
    Install polars
  </Card>
</CardGroup>
````
