Skip to main content

Data Backends in tif1

tif1 is built with a flexible backend architecture that supports two DataFrame libraries: pandas (default) and polars (optional). This design balances familiarity, ecosystem compatibility, and raw performance for each use case. The backend system is integrated into the tif1 core architecture. When tif1 loads session data, telemetry, or lap data, it constructs DataFrames with the selected backend. All subsequent operations then use the strengths of that backend. This abstraction enables a backend switch with minimal code changes and a large performance gain.

Why Multiple Backends?

The Formula 1 data analysis landscape presents unique challenges:
  • Volume: A single race session can generate over 1,200 laps. Telemetry sampled at 10-60Hz results in millions of data points.
  • Velocity: Real-time analysis during sessions requires fast data processing
  • Variety: Different analysis tasks have different requirements. Exploratory analysis benefits from the pandas ecosystem, while production pipelines need polars speed.
  • Compatibility: Many existing tools and libraries in the Python ecosystem expect pandas DataFrames
By supporting both backends, tif1 allows optimization for specific constraints without loss of functionality.

Backend Architecture Overview

The backend abstraction layer in tif1 handles the complexity of supporting multiple DataFrame libraries. When code calls get_session() or reads session attributes like .laps or .telemetry, tif1 runs these steps internally.
  1. Fetches data from the CDN or cache (backend-agnostic)
  2. Parses JSON using orjson (backend-agnostic)
  3. Constructs DataFrames using the specified backend
  4. Applies transformations (column renaming, type casting) using backend-specific code
  5. Returns typed objects with the appropriate DataFrame type
This architecture applies backend-specific optimizations at the lowest level and keeps the high-level API consistent.

Backend Comparison Matrix


Pandas Backend (Default)

Pandas is the default backend in tif1 and provides maximum compatibility with the Python data science ecosystem. Pandas has been the de facto standard for tabular data in Python since 2008. Its ecosystem of tools, libraries, and community knowledge is large.

Architecture and Design

Pandas is built on top of NumPy arrays and provides a rich, high-level API for data manipulation. Key architectural characteristics:
  • Index-based operations: Every DataFrame has an index (row labels) that enables powerful alignment and joining
  • Single-threaded execution: Operations run on a single CPU core, making behavior predictable but potentially slower
  • Eager evaluation: Operations execute immediately when called
  • Object dtype flexibility: Can store mixed types in a single column (though this impacts performance)
  • NaN for missing data: Uses NumPy’s NaN for numeric missing values, None for objects

When to Use Pandas

Pandas is the right choice when:
  • Learning tif1: The pandas API is more forgiving and has extensive documentation.
  • Ecosystem compatibility: Libraries like matplotlib, seaborn, scikit-learn, and statsmodels expect pandas DataFrames.
  • Exploratory analysis: The pandas API and Jupyter integration suit interactive work.
  • Small to medium datasets: For datasets under 1 million rows, pandas performance is typically sufficient.
  • The complete pandas API: Some advanced pandas features (MultiIndex, time series resampling, etc.) are not available in polars.
  • Integration with existing code: When a codebase already uses pandas, staying consistent reduces friction.
  • Stability: Pandas has a stable API with strong backward compatibility guarantees.

Performance Characteristics

Pandas performance varies significantly by operation type:
  • Fast operations: Vectorized numeric operations, boolean indexing, simple aggregations
  • Moderate operations: String operations, groupby with multiple aggregations, merges
  • Slow operations: Apply with Python functions, iteration, complex string parsing
Typical tif1 workloads include filtering laps, computing statistics, and grouping by driver. Pandas performs well on these workloads up to about 10,000 laps, roughly 5-8 race sessions.

Example Usage

Pandas-Specific Features in tif1

When using the pandas backend, tif1 provides additional functionality:
  • Categorical dtypes: Driver names and compounds are stored as categoricals for memory efficiency
  • Timedelta columns: Lap times are proper timedelta objects, enabling time arithmetic
  • Index preservation: tif1 maintains meaningful indices where appropriate
  • Method chaining: All tif1 DataFrame operations support pandas method chaining

Polars Backend (High-Performance)

Polars is a fast DataFrame library written in Rust and designed for performance and memory efficiency. It incorporates lessons learned from pandas and uses modern hardware capabilities.

Architecture and Design

Polars is built on Apache Arrow and uses a fundamentally different execution model:
  • Multi-threaded execution: Automatically parallelizes operations across all CPU cores
  • Apache Arrow memory format: Columnar, cache-friendly layout with zero-copy operations
  • Query optimization: Analyzes and optimizes query plans before execution (in lazy mode)
  • SIMD vectorization: Uses CPU SIMD instructions for 4-8x faster operations
  • Proper null handling: Native null support without NaN confusion
  • Rust implementation: Memory-safe, no GIL limitations, predictable performance

When to Use Polars

Polars is the right choice when:
  • Large datasets: For datasets over 1 million rows, polars parallelization performs best.
  • Performance is critical: Production pipelines need maximum throughput.
  • Memory is constrained: Polars uses 50-70% less memory than pandas for equivalent operations.
  • Data pipelines: Lazy evaluation enables query optimization.
  • Heavy aggregations: Groupby operations run 3-5x faster than pandas.
  • String work: Polars has highly optimized string kernels.
  • Predictable performance: The Rust implementation eliminates GIL and memory management issues.
  • A different API: The polars API is more explicit but requires learning.

Performance Characteristics

Polars performs well in every operation type and shows the largest gains in these areas:
  • Fastest operations: Groupby aggregations, joins, sorting, filtering large datasets
  • Strong operations: String operations, window functions, complex expressions
  • Good operations: Simple selections, arithmetic, type conversions
For tif1 workloads, polars typically provides:
  • 2-3x speedup for single-session analysis
  • 3-5x speedup for multi-session aggregations
  • 4-8x speedup for complex groupby operations
  • 2x memory reduction for typical race data

Installation

Polars is an optional dependency. Install it alongside tif1:
Polars requires Python 3.8 or later. The library is distributed as pre-built wheels for all major platforms (Linux, macOS, Windows) and architectures (x86_64, ARM64).

Example Usage

Polars-Specific Features in tif1

When using the polars backend, tif1 uses these polars capabilities:
  • Lazy evaluation: Use backend="polars" with lazy=True for query optimization
  • Parallel execution: All operations automatically use available CPU cores
  • Expression API: Complex calculations can be expressed as optimized expressions
  • Efficient string operations: Driver name filtering and compound matching are faster
  • Native Arrow: Zero-copy interop with other Arrow-based tools

Detailed Performance Comparison

Performance varies by operation type, data size, and hardware. The benchmarks below were measured on a modern 8-core CPU with a full race session dataset.

Benchmark Methodology

  • Hardware: Intel i7-12700K (8P+4E cores), 32GB DDR5 RAM
  • Dataset: 2024 Abu Dhabi GP Race session (20 drivers, 1,142 laps, full telemetry)
  • Data size: ~450MB in pandas, ~220MB in polars
  • Measurement: Median of 10 runs, cold cache
  • Python: 3.12, pandas 3.0.5, polars 1.43.2

Operation-Level Benchmarks

Benchmarks are representative but vary with hardware, data characteristics, and specific operations. Always profile the actual workloads.

Real-World Use Case Benchmarks

These benchmarks represent complete analysis workflows:

Use Case 1: Single Session Analysis

  • Pandas: 2.8s total (2.5s load, 0.3s analysis)
  • Polars: 1.4s total (1.2s load, 0.2s analysis)
  • Speedup: 2.0x

Use Case 2: Multi-Session Aggregation

  • Pandas: 68s total (24 races × ~2.8s)
  • Polars: 22s total (24 races × ~0.9s)
  • Speedup: 3.1x

Use Case 3: Telemetry Analysis

  • Pandas: 8.5s total (2.5s load, 6.0s analysis)
  • Polars: 3.2s total (1.2s load, 2.0s analysis)
  • Speedup: 2.7x

Scaling Characteristics

How performance scales with data size:
Polars’ advantage grows with dataset size due to better parallelization and memory efficiency. For small datasets (< 10k rows), the difference is minimal.

Comprehensive API Differences

While tif1 abstracts many differences, the API variations still matter for idiomatic code in each backend. This section compares common operations.

Data Selection and Filtering

Column Selection and Manipulation

Grouping and Aggregation

Sorting

Joining and Merging

Missing Data Handling

String Operations

Window Functions

Type Conversion


Converting Between Backends

tif1 converts DataFrames between backends. This lets different parts of a workflow use the strengths of each library.

Basic Conversion

Conversion Performance

Conversion between backends has a cost, so use it strategically:
Frequent conversions can negate the performance benefits of using polars. Design the workflow to minimize backend switches.

Zero-Copy Conversion (Arrow)

For maximum efficiency, use Apache Arrow as an intermediate format:
Zero-copy conversions avoid memory duplication and are much faster for large datasets. These conversions require compatible data types.

Hybrid Workflows

Combine backends strategically for optimal performance:

Preserving Data Types

Some data types do not convert exactly between backends:

Setting Default Backend

Configure tif1 to use a preferred backend by default. This avoids the need to specify backend= on every call.

Via Configuration File

Create or edit ~/.tif1rc (JSON format):
After this setting, all sessions use polars by default:

Via Environment Variable

Set the TIF1_BACKEND environment variable:
Environment variables take precedence over the config file.

Programmatically

Set the backend at runtime using the config API:

Per-Session Override

Override the default on a per-session basis at any time:

Configuration Precedence

When multiple configuration sources are present, tif1 uses this precedence order (highest to lowest):
  1. Explicit parameter: backend="polars" in function call
  2. Environment variable: TIF1_BACKEND=polars
  3. Config file: ~/.tif1rc setting
  4. Default: pandas

Backend-Specific Features and Capabilities

Each backend has unique features for specific use cases.

Pandas-Exclusive Features

MultiIndex (Hierarchical Indexing)

Time Series Functionality

Flexible Indexing

Rich Ecosystem Integration

Polars-Exclusive Features

Lazy Evaluation and Query Optimization

Lazy evaluation allows polars to optimize the entire query plan, potentially eliminating unnecessary operations and reducing memory usage.

Expression API

Parallel String Operations

Native Arrow Integration

Streaming Mode (Out-of-Core Processing)

Feature Comparison Table


Lazy Evaluation Deep Dive (Polars)

Lazy evaluation is a key polars feature. It enables query optimization that can improve performance by a large factor.

Understanding Lazy vs Eager Execution

Query Optimization Techniques

Polars applies several optimizations automatically:

1. Predicate Pushdown

Filters are pushed down to the data source, reading only necessary rows:

2. Projection Pushdown

Only requested columns are read from storage:

3. Common Subexpression Elimination

Repeated calculations are computed once:

4. Filter Reordering

Cheap filters run before expensive ones:

Viewing Query Plans

Examine what polars plans to do before execution:
Output:

When to Use Lazy Evaluation

Use lazy evaluation when:
  • Reading from files: Parquet, CSV, JSON (use scan_* instead of read_*)
  • Complex queries: Multiple filters, joins, aggregations
  • Large datasets: When data does not fit comfortably in memory
  • Production pipelines: Consistent performance is critical
Do not use lazy evaluation when:
  • Interactive exploration: Eager mode gives immediate feedback
  • Small datasets: The optimization overhead exceeds the gain.
  • Simple operations: Single filter or selection is already fast

Lazy Evaluation Patterns

Pattern 1: ETL Pipeline

Pattern 2: Multi-File Aggregation

Pattern 3: Streaming for Huge Datasets

Performance Impact of Lazy Evaluation

Lazy evaluation benefits increase with query complexity and data size. Simple operations may see minimal improvement.

Decision Framework: Choosing the Right Backend

Use this decision tree to select the best backend for a given use case:

Quick Reference Guide

Detailed Decision Criteria

Choose Pandas When:

  1. Ecosystem Integration is Critical
    • The analysis uses libraries that require pandas (seaborn, scikit-learn, statsmodels)
    • The team standardizes on pandas
    • Existing pandas code needs maintenance
  2. Dataset is Small to Medium
    • Under 100,000 rows: Performance difference is negligible
    • Under 1,000,000 rows: Pandas is still performant enough
  3. Specific Pandas Features
    • MultiIndex for hierarchical data
    • Advanced time series resampling
    • Flexible indexing with loc/iloc
    • Pandas-specific methods that the code depends on
  4. Learning and Exploration
    • Data analysis in Python is new work
    • The work is interactive exploration in Jupyter
    • Stack Overflow coverage is a priority

Choose Polars When:

  1. Performance is Critical
    • Data processing runs in production
    • Consistent, predictable performance is required
    • Time constraints are tight
  2. Dataset is Large
    • Over 1,000,000 rows: Polars parallelization performs best
    • Over 10,000,000 rows: Polars may be 5-10x faster
    • Data does not fit comfortably in RAM: Use streaming
  3. Memory is Constrained
    • Running on limited hardware
    • Processing multiple datasets simultaneously
    • Need to minimize cloud computing costs
  4. Building Data Pipelines
    • ETL workflows benefit from lazy evaluation
    • Query optimization reduces complexity
    • Consistent performance is more important than peak performance
  5. Heavy Aggregations or String Operations
    • Complex groupby with multiple aggregations
    • Extensive string parsing or manipulation
    • Window functions over large groups

Hybrid Approach

One backend is not required for the whole project. Use both strategically:

Advanced Patterns and Best Practices

Pattern 1: Processing Multiple Sessions Efficiently

When analyzing multiple sessions, backend choice significantly impacts performance:

Pattern 2: Memory-Efficient Telemetry Analysis

Telemetry data can be very large. This pattern handles it efficiently:

Pattern 3: Incremental Data Processing

Build results incrementally without loading everything at once:

Pattern 4: Optimizing Groupby Operations

Groupby operations are common in F1 analysis. Optimize them:

Pattern 5: Efficient Filtering

Apply filters efficiently to minimize data scanned:

Pattern 6: Caching Expensive Computations

Cache results of expensive operations:

Best Practices Summary

  • Use polars for large datasets (50-70% less memory)
  • Process data incrementally when possible
  • Use lazy evaluation for multi-step operations
  • Free memory explicitly with del when done with large objects
  • Monitor memory usage with memory_profiler or similar tools
  • Profile before optimizing (use cProfile or line_profiler)
  • Use polars for operations on > 100k rows
  • Combine multiple aggregations in single groupby
  • Use lazy evaluation for complex queries
  • Avoid Python loops over rows (use vectorized operations)
  • Cache expensive computations with @lru_cache
  • Set default backend in config file for consistency
  • Document backend choice in function docstrings
  • Use type hints: pd.DataFrame or pl.DataFrame
  • Create backend-agnostic interfaces when possible
  • Test with both backends if supporting both
  • Use polars for ETL pipelines
  • Save intermediate results to Parquet format
  • Use lazy evaluation for multi-file processing
  • Implement streaming for datasets larger than RAM
  • Monitor pipeline performance with logging
  • Use .explain() to understand polars query plans
  • Compare results between backends for validation
  • Use .describe() to check data distributions
  • Profile memory usage with both backends
  • Test edge cases (empty DataFrames, single row, nulls)

Troubleshooting and Common Issues

Issue 1: Polars Not Installed

Symptom: ImportError: No module named 'polars' or BackendNotAvailableError Solution:

Issue 2: Type Errors After Backend Switch

Symptom: Code works with pandas but fails with polars (or vice versa) Solution: Check for backend-specific API usage

Issue 3: Memory Issues with Pandas

Symptom: MemoryError or system slowdown with large datasets Solution: Switch to polars or use chunking

Issue 4: Slow Groupby Operations

Symptom: Groupby takes several seconds with pandas Solution: Use polars for 3-5x speedup

Issue 5: Conversion Overhead

Symptom: Frequent conversions between backends slow down code Solution: Minimize conversions, do them once

Issue 6: Lazy Evaluation Confusion

Symptom: A polars lazy query does not return data Solution: Remember to call .collect()

Issue 7: Incompatible Library Expectations

Symptom: Library expects pandas DataFrame but receives polars Solution: Convert before passing to library

Issue 8: Column Name Conflicts

Symptom: Errors with column names containing spaces or special characters Solution: Use proper column selection syntax

Issue 9: Performance Not Improving with Polars

Symptom: Polars is not faster than pandas for the given use case Possible Causes:
  1. Dataset is too small (< 10k rows)
  2. Using eager mode instead of lazy
  3. Bottleneck is I/O, not computation
  4. Single-threaded operations (some string ops)
Solution: Profile and optimize

Issue 10: Type Inference Differences

Symptom: Same data has different types in pandas vs polars Solution: Explicitly cast types

Common Use Cases

Performance guide.

Performance

Optimization.

Installation

Install polars.

Summary and Key Takeaways

Quick Summary

  • Pandas: Default backend, maximum compatibility, gentle learning curve
  • Polars: High-performance backend, 2-5x faster, 50-70% less memory
  • Choice: Depends on dataset size, performance needs, and ecosystem requirements
  • Flexibility: Use both in the same project, convert as needed
  • Recommendation: Start with pandas, migrate to polars for performance-critical code

Key Performance Insights

  1. Polars performs best with large datasets: 3-5x speedup for > 100k rows
  2. Memory efficiency matters: Polars uses 50-70% less memory
  3. Lazy evaluation improves speed: Query optimization can double performance
  4. Conversion has cost: Minimize backend switches
  5. Ecosystem compatibility: Pandas still dominates for visualization and ML

When to Use Each Backend

Use Pandas for:
  • Learning and exploration
  • Small to medium datasets (< 100k rows)
  • Ecosystem integration (seaborn, scikit-learn)
  • Time series analysis
  • Hierarchical data (MultiIndex)
Use Polars for:
  • Large datasets (> 100k rows)
  • Production pipelines
  • Memory-constrained environments
  • Heavy aggregations
  • String-intensive operations

Best Practices Checklist

Next Steps

1

Experiment

Try both backends with typical workloads to measure the actual performance differences.
2

Profile

Use profiling tools to identify bottlenecks in the code.
3

Optimize

Apply backend-specific optimizations (lazy evaluation, vectorization).
4

Monitor

Track performance and memory usage in production.
5

Iterate

Continuously refine the backend choice based on real-world results.

Installation Guide

Install tif1 with polars support.

Performance Guide

Optimization techniques and best practices.

API Reference

Complete API documentation.

Data Flow

The tif1 data architecture.

Caching Strategy

How tif1 caches data efficiently.

Common Use Cases

Real-world examples and patterns.

Additional Resources

External Documentation

Performance Benchmarks

Community Resources


This documentation is maintained as part of the tif1 project. For questions or suggestions, please open an issue on GitHub.
Last modified on September 3, 2026