.. _api_vectorizers:

===========
Vectorizers
===========

.. currentmodule:: medtda.vectorizers

The ``vectorizers`` module provides 8 different methods to convert persistence barcodes into fixed-length feature vectors.

.. contents:: Contents
   :local:
   :depth: 2

Overview
========

Persistence barcodes are variable-length representations that need to be vectorized for machine learning. This module provides multiple vectorization strategies, each with different properties.

**Available Methods:**

1. ``persistence_stats`` - Statistical summaries (fast, interpretable)
2. ``betti_curve`` - Betti numbers over filtration values
3. ``persistence_image`` - 2D histogram with Gaussian weighting
4. ``persistence_landscape`` - Functional representation
5. ``persistence_silhouette`` - Average persistence landscape  
6. ``entropy_summary`` - Information-theoretic features
7. ``persistence_lifespan`` - Lifespan distribution
8. ``persistence_tropical_coordinates`` - Tropical algebra representation

Quick Comparison
================

.. list-table::
   :widths: 25 15 15 45
   :header-rows: 1

   * - Method
     - Speed
     - Size
     - Best For
   * - ``persistence_stats``
     - Fast
     - dict (38 features)
     - Statistical summaries
   * - ``betti_curve``
     - Fast
     - Medium (100)
     - Betti number evolution
   * - ``persistence_image``
     - Medium
     - Large (400)
     - 2D histogram representation
   * - ``persistence_landscape``
     - Medium
     - Large (500+)
     - Piecewise-linear landscape functions
   * - ``persistence_silhouette``
     - Medium
     - Medium (100)
     - Weighted average landscape
   * - ``entropy_summary``
     - Medium
     - Medium (100)
     - Entropy-based summary
   * - ``persistence_lifespan``
     - Fast
     - Medium (100)
     - Lifespan distribution
   * - ``persistence_tropical_coordinates``
     - Fast
     - 7
     - Tropical polynomial invariants

Functions
=========

persistence_stats
-----------------

.. autofunction:: medtda.vectorizers.persistence_stats

Compute statistical summaries of persistence values.

**Signature:**

.. code-block:: python

   def persistence_stats(barcode: np.ndarray) -> np.ndarray

**Parameters:**

* **barcode** (*np.ndarray*) - Barcode array of shape ``(n, 2)`` with ``[birth, death]`` columns

**Returns:**

* **stats** (*dict*) - Dictionary of 38 statistical features. Keys cover births, deaths,
  midpoints, and lifespans with statistics: mean, std, median, IQR, range, and
  percentiles (10, 25, 75, 90), plus ``count`` and ``entropy``.

**Example:**

::

    from medtda.vectorizers import persistence_stats

    stats = persistence_stats(barcode)
    print(f"Feature count: {len(stats)}")  # 38
    
    # stats contains named features:
    # - stats['births_mean'], stats['births_std'], ...
    # - stats['lifespans_mean'], ...
    # - stats['count'], stats['entropy']

betti_curve
-----------

.. autofunction:: medtda.vectorizers.betti_curve

Compute Betti numbers as a function of filtration value.

**Signature:**

.. code-block:: python

   def betti_curve(
       barcode: np.ndarray,
       resolution: int = 100
   ) -> np.ndarray

**Parameters:**

* **barcode** (*np.ndarray*) - Barcode array
* **resolution** (*int*) - Number of sample points (default: 100)

**Returns:**

* **curve** (*np.ndarray*) - 1D array of Betti numbers at each filtration value

**Example:**

::

    from medtda.vectorizers import betti_curve

    # Default resolution
    curve = betti_curve(h1_barcode)
    print(f"Curve shape: {curve.shape}")  # (100,)

    # Higher resolution for more detail
    curve_hires = betti_curve(h1_barcode, resolution=200)

persistence_image
-----------------

.. autofunction:: medtda.vectorizers.persistence_image

Create a 2D histogram representation weighted by persistence.

**Signature:**

.. code-block:: python

   def persistence_image(
       barcode: np.ndarray,
       bandwidth: float = 0.2,
       resolution: int = 20
   ) -> np.ndarray

**Parameters:**

* **barcode** (*np.ndarray*) - Barcode array
* **bandwidth** (*float*) - Gaussian kernel bandwidth (default: 0.2)
* **resolution** (*int*) - Grid resolution (default: 20)

**Returns:**

* **image** (*np.ndarray*) - 2D array of shape ``(resolution, resolution)``

**Example:**

::

    from medtda.vectorizers import persistence_image

    # Default parameters
    pi = persistence_image(barcode)
    print(f"Image shape: {pi.shape}")  # (20, 20)

    # Higher resolution, smaller bandwidth
    pi_hires = persistence_image(
        barcode,
        bandwidth=0.1,
        resolution=30
    )

persistence_landscape
----------------------

.. autofunction:: medtda.vectorizers.persistence_landscape

Compute persistence landscape representation.

**Signature:**

.. code-block:: python

   def persistence_landscape(
       barcode: np.ndarray,
       resolution: int = 100,
       num_landscapes: int = 5
   ) -> np.ndarray

**Parameters:**

* **barcode** (*np.ndarray*) - Barcode array
* **resolution** (*int*) - Number of sample points (default: 100)
* **num_landscapes** (*int*) - Number of landscape functions (default: 5)

**Returns:**

* **landscape** (*np.ndarray*) - 1D array of length ``num_landscapes * resolution``

**Example:**

::

    from medtda.vectorizers import persistence_landscape

    landscape = persistence_landscape(barcode)
    print(f"Shape: {landscape.shape}")  # (500,)

    # Reshape for visualization
    landscape_2d = landscape.reshape(5, 100)  # (num_landscapes, resolution)

persistence_silhouette
----------------------

.. autofunction:: medtda.vectorizers.persistence_silhouette

Compute persistence silhouette (average landscape).

**Signature:**

.. code-block:: python

   def persistence_silhouette(
       barcode: np.ndarray,
       resolution: int = 100,
       weight: float = 1.0
   ) -> np.ndarray

**Parameters:**

* **barcode** (*np.ndarray*) - Barcode array
* **resolution** (*int*) - Number of sample points (default: 100)
* **weight** (*float*) - Weighting power (default: 1.0)

**Returns:**

* **silhouette** (*np.ndarray*) - 1D array of length ``resolution``

**Example:**

::

    from medtda.vectorizers import persistence_silhouette

    silhouette = persistence_silhouette(barcode, resolution=150)

entropy_summary
---------------

.. autofunction:: medtda.vectorizers.entropy_summary

Compute entropy-based features.

**Signature:**

.. code-block:: python

   def entropy_summary(
       barcode: np.ndarray,
       resolution: int = 100
   ) -> np.ndarray

**Parameters:**

* **barcode** (*np.ndarray*) - Barcode array  
* **resolution** (*int*) - Number of bins (default: 100)

**Returns:**

* **entropy_features** (*np.ndarray*) - 1D array of entropy-based features

**Example:**

::

    from medtda.vectorizers import entropy_summary

    entropy_feats = entropy_summary(barcode)

persistence_lifespan
--------------------

.. autofunction:: medtda.vectorizers.persistence_lifespan

Compute distribution of feature lifespans.

**Signature:**

.. code-block:: python

   def persistence_lifespan(
       barcode: np.ndarray,
       resolution: int = 100
   ) -> np.ndarray

**Parameters:**

* **barcode** (*np.ndarray*) - Barcode array
* **resolution** (*int*) - Number of bins (default: 100)

**Returns:**

* **lifespan_dist** (*np.ndarray*) - 1D array of length ``resolution``

**Example:**

::

    from medtda.vectorizers import persistence_lifespan

    lifespan = persistence_lifespan(barcode)

persistence_tropical_coordinates
---------------------------------

.. autofunction:: medtda.vectorizers.persistence_tropical_coordinates

Compute tropical coordinates representation.

**Signature:**

.. code-block:: python

   def persistence_tropical_coordinates(
       barcode: np.ndarray,
       r: int = 28
   ) -> np.ndarray

**Parameters:**

* **barcode** (*np.ndarray*) - Barcode array
* **r** (*int*) - Parameter for tropical coordinate computation (default: 28)

**Returns:**

* **coords** (*np.ndarray*) - 1D array of length 7

**Example:**

::

    from medtda.vectorizers import persistence_tropical_coordinates

    coords = persistence_tropical_coordinates(barcode, r=28)
    print(f"Coordinate vector size: {len(coords)}")  # 7

Complete Examples
=================

Example 1: Single Method
-------------------------

::

    from medtda import FeatureExtractor
    from medtda.vectorizers import persistence_image

    # Extract barcodes
    extractor = FeatureExtractor(return_barcodes=True)
    features, barcodes = extractor.execute('image.nii.gz')

    # Manually vectorize H1 barcode
    h1_barcode = barcodes['H1']
    pi_features = persistence_image(h1_barcode, bandwidth=0.15, resolution=25)

    print(f"Persistence image size: {pi_features.shape}")

Example 2: Multiple Methods
----------------------------

::

    from medtda.vectorizers import (
        persistence_stats,
        betti_curve,
        persistence_image
    )
    import numpy as np

    # Get barcodes
    h1 = barcodes['H1']

    # Apply multiple vectorizations
    stats = persistence_stats(h1)       # returns dict of 38 features
    curve = betti_curve(h1, resolution=100)
    image = persistence_image(h1, resolution=20)

    # Concatenate for ML
    features = np.concatenate([
        list(stats.values()),
        curve,
        image.flatten()
    ])

    print(f"Combined feature vector size: {len(features)}")

Example 3: Comparing Methods
-----------------------------

::

    from medtda.vectorizers import *
    import numpy as np

    methods = {
        'stats': persistence_stats,
        'betti': lambda b: betti_curve(b, resolution=100),
        'pi': lambda b: persistence_image(b, resolution=20).flatten(),
        'landscape': lambda b: persistence_landscape(b).flatten(),
        'silhouette': lambda b: persistence_silhouette(b, resolution=100),
        'entropy': lambda b: entropy_summary(b, resolution=100),
        'lifespan': lambda b: persistence_lifespan(b, resolution=100),
        'tropical': lambda b: persistence_tropical_coordinates(b, r=28)
    }

    for name, method in methods.items():
        features = method(h1_barcode)
        print(f"{name:12s}: {len(features):4d} features")

Example 4: Custom Parameters
-----------------------------

::

    from medtda.vectorizers import persistence_image, betti_curve

    # Experiment with different parameters
    
    # Coarse persistence image (fast)
    pi_coarse = persistence_image(barcode, resolution=15, bandwidth=0.3)
    
    # Fine persistence image (detailed)
    pi_fine = persistence_image(barcode, resolution=30, bandwidth=0.1)
    
    # Low-resolution Betti curve
    bc_low = betti_curve(barcode, resolution=50)
    
    # High-resolution Betti curve
    bc_high = betti_curve(barcode, resolution=200)

Example 5: Multi-Dimensional Barcodes
--------------------------------------

::

    from medtda import BarcodeExtractor
    from medtda.vectorizers import persistence_image
    import numpy as np

    # Extract all dimensions
    extractor = BarcodeExtractor(max_dimension=2)
    barcodes = extractor.execute(image_3d)

    # Vectorize each dimension separately
    features_by_dim = {}
    for dim in [0, 1, 2]:
        if dim in barcodes and len(barcodes[dim]) > 0:
            features_by_dim[f'H{dim}'] = persistence_image(
                barcodes[dim],
                resolution=20
            ).flatten()

    # Concatenate all dimensions
    all_features = np.concatenate([
        features_by_dim['H0'],
        features_by_dim['H1'],
        features_by_dim['H2']
    ])

    print(f"Total feature vector size: {len(all_features)}")

See Also
========

* :doc:`featureextractor` - Automatic vectorization
* :doc:`barcodeextractor` - Extract barcodes
* :doc:`../user_guide/vectorization` - Detailed vectorization guide
* :doc:`../theory/vectorization_methods` - Mathematical background
