Search

Search IconIcon to open search

Fixed-Size sample (Reservoir Sampling)

Last updatedUpdated: by Jakub Žovák · 2 min read

Properties
created 31.12.2024, 13:11
modified 08.08.2025, 19:04
published Empty
sources PA152 Course
topics Reservoir Sampling, Stream Sampling, Data Streams
authors Empty
ai-assisted No

# Reservoir Sampling

  • Store all the first $s$ elements of the stream to $S$.
  • Suppose we have seen $n-1$ elements, and now the $n^\text{th}$ element arrives ($n > s$):
    • With probability $\frac{s}{n}$, keep the $n^\text{th}$ element; else discard it.
    • If we picked the $n^\text{th}$ element, then it replaces one of the $s$ elements in the sample $S$, picked uniformly at random

This algorithm maintains a sample $S$ with the desired property:

  • After $n$ elements, the sample contains each element seen so far with probability $\frac{s}{n}$.

# Python Implementation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import random
from typing import List, Any

class ReservoirSampler:
    def __init__(self, k: int):
        """
        Initializes the reservoir sampler.
        
        Args:
            k (int): The number of items to maintain in the reservoir.
        """
        self.k = k
        self.reservoir = []
        self.stream_size = 0  # Tracks the number of items processed in the stream
    
    def add_to_reservoir(self, new_item: Any) -> None:
        """
        Processes a new item from the stream and updates the reservoir.
        
        Args:
            new_item (Any): The new item from the stream to process.
        """
        self.stream_size += 1
        
        if len(self.reservoir) < self.k:
            # Fill the reservoir until it reaches size k
            self.reservoir.append(new_item)
        else:
            # Replace an existing item with a decreasing probability
            j = random.randint(1, self.stream_size)
            if j <= self.k:
                self.reservoir[j - 1] = new_item

    def get_reservoir(self) -> List[Any]:
        """
        Returns the current reservoir.
        
        Returns:
            List[Any]: The list of items in the reservoir.
        """
        return self.reservoir