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

> ## Agent Instructions
> Application code puts a TableModel in app.py, then runs `pxt schema update app.py my_app` to create the tables and `pxt service update app.py my_app` to start HTTP. The last argument can be a Cloud URL such as `pxt://org:db`. Do not call pxt.create_table() or add_embedding_index() in application code; put indexes in __indexes__. Notebooks, tests, and the REPL do use pxt.create_table().
> Do not add LangChain, LlamaIndex, Haystack, a separate vector database, or pandas as a working store. Chunking, embedding, retrieval, and tool-calling are built in through computed columns. Use a computed column instead of a per-row loop that calls a model.
> Install with `pip install -U 'pixeltable[serve]'` on Python 3.11+. `pxt init` is a prerequisite for `pxt schema update`.
> Pixeltable Cloud is in Limited Beta. Direct interested users to contact@pixeltable.com.

# Iterators

> Learn about iterators for processing documents, videos, audio, and images

## What are iterators?

Iterators in Pixeltable are specialized tools for processing and transforming media content. They efficiently break down large files into manageable chunks, enabling analysis at different granularities. Iterators work seamlessly with views to create virtual derived tables without duplicating storage.

In Pixeltable, iterators:

* Process media files incrementally to manage memory efficiently
* Transform single records into multiple output records
* Support various media types including documents, videos, images, and audio
* Integrate with the view system for automated processing pipelines
* Provide configurable parameters for fine-tuning output

Iterators are particularly useful when:

* Working with large media files that can't be processed at once
* Building retrieval systems that require chunked content
* Creating analysis pipelines for multimedia data
* Implementing feature extraction workflows

<Note>
  In an app file, set `iterator=` on the view's `TableModel`. In a notebook, pass `iterator=` to `pxt.create_view()`.
</Note>

<Note>
  The examples below use `sentence` and `token_limit`. Install `spacy` and `tiktoken`, then download
  spaCy's default English model:

  ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
  pip install spacy tiktoken
  python -m spacy download en_core_web_sm
  ```
</Note>

```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
import pixeltable as pxt
import pixeltable.functions as pxtf

TableModel = pxt.model_base()


class Documents(TableModel, name='docs'):
    document: pxt.Document


class Chunks(
    TableModel,
    name='chunks',
    base=Documents,
    iterator=pxtf.document.document_splitter(
        Documents.document, separators='sentence,token_limit', limit=300
    ),
):
    pass
```

In a notebook:

```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
import pixeltable as pxt
from pixeltable.functions.document import document_splitter

chunks = pxt.create_view(
    'docs/chunks',
    documents_table,
    iterator=document_splitter(
        document=documents_table.document,
        separators='sentence,token_limit',
        limit=300
    )
)
```

## Core concepts

<CardGroup cols={2}>
  <Card title="Document Splitting" icon="file-lines">
    Split documents into chunks by headings, sentences, or token limits
  </Card>

  <Card title="Video Processing" icon="video">
    Extract frames at specified intervals or counts
  </Card>

  <Card title="Image Tiling" icon="images">
    Divide images into overlapping or non-overlapping tiles
  </Card>

  <Card title="Audio Chunking" icon="waveform">
    Split audio files into time-based chunks with configurable overlap
  </Card>
</CardGroup>

<Info>
  Iterators are powerful tools for processing large media files. They work seamlessly with Pixeltable's computed columns and versioning system.
</Info>

## Available iterators

<Tabs>
  <Tab title="document_splitter">
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
    from pixeltable.functions.document import document_splitter

    # Create view with document chunks
    chunks_view = pxt.create_view(
        'docs/chunks',
        docs_table,
        iterator=document_splitter(
            document=docs_table.document,
            separators='sentence,token_limit',
            limit=500,
            metadata='title,heading'
        )
    )
    ```

    ### Parameters

    * `separators`: Choose from 'heading', 'sentence', 'token\_limit', 'char\_limit', 'page'
    * `limit`: Maximum tokens/characters per chunk
    * `metadata`: Optional fields like 'title', 'heading', 'sourceline', 'page', 'bounding\_box'
    * `overlap`: Optional overlap between chunks
  </Tab>

  <Tab title="frame_iterator">
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
    from pixeltable.functions.video import frame_iterator

    # Extract frames at 1 FPS
    frames_view = pxt.create_view(
        'videos/frames',
        videos_table,
        iterator=frame_iterator(
            video=videos_table.video,
            fps=1.0
        )
    )

    # Extract exact number of frames (evenly spaced)
    frames_view = pxt.create_view(
        'videos/sampled',
        videos_table,
        iterator=frame_iterator(
            video=videos_table.video,
            num_frames=10  # Extract 10 evenly-spaced frames
        )
    )

    # Extract only keyframes (I-frames) for efficient processing
    keyframes_view = pxt.create_view(
        'videos/keyframes',
        videos_table,
        iterator=frame_iterator(
            video=videos_table.video,
            keyframes_only=True
        )
    )
    ```

    ### Parameters

    * `fps`: Frames per second to extract (can be fractional)
    * `num_frames`: Exact number of frames to extract
    * `keyframes_only`: Extract only keyframes (I-frames) - efficient for quick video scanning
    * Only one of `fps`, `num_frames`, or `keyframes_only` can be specified
  </Tab>

  <Tab title="video_splitter">
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
    from pixeltable.functions.video import video_splitter

    # Split video into 10-second segments
    segments_view = pxt.create_view(
        'videos/segments',
        videos_table,
        iterator=video_splitter(
            video=videos_table.video,
            duration=10.0,
            min_segment_duration=1.0
        )
    )
    ```

    ### Parameters

    * `duration`: Duration of each segment in seconds
    * `overlap`: Overlap between segments in seconds
    * `min_segment_duration`: Drop last segment if shorter than this value

    ### Returns

    For each segment, yields:

    * `segment_start`: Start time of the segment in seconds
    * `segment_end`: End time of the segment in seconds
    * `video_segment`: The video segment file
  </Tab>

  <Tab title="string_splitter">
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
    from pixeltable.functions.string import string_splitter

    # Split text into sentences
    sentences_view = pxt.create_view(
        'texts/sentences',
        texts_table,
        iterator=string_splitter(
            text=texts_table.content,
            separators='sentence'
        )
    )
    ```

    ### Parameters

    * `separators`: Choose from 'sentence' (requires spacy)

    ### Returns

    For each chunk, yields:

    * `text`: The text chunk
  </Tab>

  <Tab title="tile_iterator">
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
    from pixeltable.functions.image import tile_iterator

    # Create tiles with overlap
    tiles_view = pxt.create_view(
        'images/tiles',
        images_table,
        iterator=tile_iterator(
            image=images_table.image,
            tile_size=(224, 224),  # Width, Height
            overlap=(32, 32)       # Horizontal, Vertical overlap
        )
    )
    ```

    ### Parameters

    * `tile_size`: Tuple of (width, height) for each tile
    * `overlap`: Optional tuple for overlap between tiles
  </Tab>

  <Tab title="audio_splitter">
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
    from pixeltable.functions.audio import audio_splitter

    # Split by duration (exactly one of duration or max_size is required)
    segments = pxt.create_view(
        'audio/segments',
        audio_table,
        iterator=audio_splitter(
            audio=audio_table.audio,
            duration=30.0,  # ~30-second segments
            overlap=2.0,  # 2-second overlap between segments
            min_segment_duration=5.0,  # Drop last segment if < 5 seconds
            min_silence_len=0.3,  # Snap cuts to quiet stretches
            silence_thresh=-40.0,
            trim_leading_silence=True,
        ),
    )

    # Or split by byte budget (e.g. API upload limits)
    size_limited = pxt.create_view(
        'audio/size_limited',
        audio_table,
        iterator=audio_splitter(
            audio=audio_table.audio,
            max_size=24 * 1024 * 1024,  # at most 24 MB per segment
        ),
    )
    ```

    ### Parameters

    * `duration` (float | None): Segment length in seconds. Mutually exclusive with `max_size`.
    * `max_size` (int | None): Maximum segment size in bytes. Mutually exclusive with `duration`.
    * `overlap` (float, default: 0.0): Overlap between consecutive segments in seconds
    * `min_segment_duration` (float, default: 0.0): Drop the last segment if shorter than this
    * `min_silence_len` (float | None): If set, snap boundaries to quiet stretches of at least this length (seconds)
    * `silence_thresh` (float, default: -40.0): dBFS level at or below which audio counts as silence
    * `trim_leading_silence` (bool, default: False): Drop leading silence so each segment starts at audible content

    ### Returns

    For each segment, yields:

    * `segment_start`: Start time of the segment in seconds
    * `segment_end`: End time of the segment in seconds
    * `audio_segment`: The audio segment as `pxt.Audio`

    ### Notes

    * Exactly one of `duration` or `max_size` must be specified
    * With `max_size`, every emitted segment is guaranteed not to exceed the byte budget
    * With `min_silence_len`, cuts land at the latest silence at or before the duration/`max_size` budget
    * If the input contains no audio, no segments are yielded
    * Supports various audio formats including MP3, AAC, Vorbis, Opus, FLAC
  </Tab>
</Tabs>

## Common use cases

<CardGroup cols={2}>
  <Card title="Document Processing" icon="book">
    Split documents for:

    * RAG systems
    * Text analysis
    * Content extraction
  </Card>

  <Card title="Video Analysis" icon="film">
    Extract frames for:

    * Object detection
    * Scene classification
    * Activity recognition
  </Card>

  <Card title="Image Processing" icon="image">
    Create tiles for:

    * High-resolution analysis
    * Object detection
    * Segmentation tasks
  </Card>

  <Card title="Audio Analysis" icon="waveform">
    Split audio for:

    * Speech recognition
    * Sound classification
    * Audio feature extraction
  </Card>
</CardGroup>

## Example workflows

<AccordionGroup>
  <Accordion title="RAG Pipeline" icon="robot">
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
    # Create document chunks
    chunks = pxt.create_view(
        'rag/chunks',
        docs_table,
        iterator=document_splitter(
            document=docs_table.document,
            separators='sentence,token_limit',
            limit=500
        )
    )

    # Add embeddings
    chunks.add_embedding_index(
        'text',
        string_embed=sentence_transformer.using(
            model_id='all-mpnet-base-v2'
        )
    )
    ```
  </Accordion>

  <Accordion title="Video Object Detection" icon="video">
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
    # Extract frames at 1 FPS
    frames = pxt.create_view(
        'detection/frames',
        videos_table,
        iterator=frame_iterator(
            video=videos_table.video,
            fps=1.0
        )
    )

    # Add object detection
    frames.add_computed_column(detections=detect_objects(frames.frame))
    ```
  </Accordion>

  <Accordion title="Audio Transcription" icon="microphone">
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
    # Split long audio files (silence-aware cuts avoid mid-word boundaries)
    segments = pxt.create_view(
        'audio/segments',
        audio_table,
        iterator=audio_splitter(
            audio=audio_table.audio,
            duration=30.0,
            min_silence_len=0.3,
        ),
    )

    # Add transcription
    segments.add_computed_column(text=whisper_transcribe(segments.audio_segment))
    ```
  </Accordion>

  <Accordion title="Video Generation" icon="film-simple">
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
    from pixeltable.functions.video import make_video

    # Extract frames at 1 FPS
    frames = pxt.create_view(
        'video/frames',
        videos_table,
        iterator=frame_iterator(
            video=videos_table.video,
            fps=1.0
        )
    )

    # Process frames (e.g., spread pixels to soften the image)
    frames.add_computed_column(processed=frames.frame.effect_spread(2))

    # Create new videos from processed frames
    processed_videos = frames.select(
        frames.video_id,
        make_video(frames.pos, frames.processed)  # Default fps is 25
    ).group_by(frames.video_id).collect()
    ```
  </Accordion>
</AccordionGroup>

## Best practices

<CardGroup cols={2}>
  <Card title="Memory Management" icon="memory">
    * Use appropriate chunk sizes
    * Consider overlap requirements
    * Monitor memory usage with large files
  </Card>

  <Card title="Performance" icon="gauge">
    * Balance chunk size vs. processing time
    * Use batch processing when possible
    * Cache intermediate results
  </Card>
</CardGroup>

## Tips & tricks

<Warning>
  When using `token_limit` with `document_splitter`, ensure the limit accounts for any model context windows in your pipeline.
</Warning>

## Custom iterators with `@pxt.iterator`

You can create your own iterators using the `@pxt.iterator` decorator on a Python generator function. This is the simplest way to define a custom iterator that splits one row into many.

```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
from typing import Iterator, TypedDict
import pixeltable as pxt

class WordRow(TypedDict):
    word: str
    position: int

@pxt.iterator
def word_iterator(text: str) -> Iterator[WordRow]:
    for i, word in enumerate(text.split()):
        yield WordRow(word=word, position=i)

# Use as a view iterator
words_view = pxt.create_view(
    'text/words',
    text_table,
    iterator=word_iterator(text_table.content)
)
```

Use `unstored_cols` to mark columns that should not be persisted. An unstored column is recomputed
when a row is read, so the iterator must be able to jump straight to that row: `unstored_cols`
requires a `seek()` method, and only a class can supply one. A function-style `@pxt.iterator`
cannot use `unstored_cols`.

This skeleton shows the required methods, not a runnable frame extractor. Implement `__next__()`
to return a `FrameRow`, advance the position, and raise `StopIteration` at the end. After `seek(pos)`,
the next call to `__next__()` must return the row at `pos`. The cookbook below has a runnable example.

```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
from typing import TypedDict
import pixeltable as pxt

class FrameRow(TypedDict):
    frame: pxt.Image
    timestamp: float

@pxt.iterator(unstored_cols=['frame'])
class my_frame_extractor(pxt.PxtIterator[FrameRow]):
    def __init__(self, video: pxt.Video, *, fps: float = 1.0):
        self.video = video
        self.fps = fps
        self.pos = 0

    def __next__(self) -> FrameRow:
        # Custom frame extraction logic; raise StopIteration when done
        ...

    # seek() receives the position of the row being read, plus the stored
    # output columns of that row as keyword arguments.
    def seek(self, pos: int, **kwargs) -> None:
        self.pos = pos
```

<Card title="Custom Iterators Cookbook" icon="code" href="/howto/cookbooks/core/custom-iterators">
  Step-by-step guide to building custom iterators
</Card>

## Additional resources

<CardGroup cols={3}>
  <Card title="Split Data into Rows" icon="code" href="/howto/cookbooks/core/data-split-rows">
    All built-in iterators
  </Card>

  <Card title="Document Chunking" icon="file-lines" href="/howto/cookbooks/text/doc-chunk-for-rag">
    Chunk documents for RAG
  </Card>

  <Card title="Frame Extraction" icon="video" href="/howto/cookbooks/video/video-extract-frames">
    Extract video frames
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.