Claude Code Plugins

Community-maintained marketplace

Feedback

parameterization

@cdcore09/holoviz-claude
2
0

Master declarative parameter systems with Param for type-safe configuration. Use this skill when building parameterized classes with automatic validation, creating reactive dependencies with @param.depends, implementing watchers for side effects, auto-generating UIs from parameters, or organizing application configuration with hierarchical parameter structures.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name parameterization
description Master declarative parameter systems with Param for type-safe configuration. Use this skill when building parameterized classes with automatic validation, creating reactive dependencies with @param.depends, implementing watchers for side effects, auto-generating UIs from parameters, or organizing application configuration with hierarchical parameter structures.
compatibility Requires param >= 2.0.0, panel >= 1.3.0 (for UI generation), numpy >= 1.15.0, pandas >= 1.0.0

Parameterization Skill

Overview

Master declarative parameter systems with Param and dynamic UI generation. This skill covers building flexible, type-safe, and auto-validated application logic.

Dependencies

  • param >= 2.0.0
  • panel >= 1.3.0 (for UI generation)
  • numpy >= 1.15.0
  • pandas >= 1.0.0

Core Capabilities

1. Parameter Basics

Param provides a framework for parameterized objects with automatic validation:

import param
import numpy as np

class DataProcessor(param.Parameterized):
    # Basic parameters
    name = param.String(default='Processor', doc='Name of processor')
    count = param.Integer(default=10, bounds=(1, 1000), doc='Number of items')
    scale = param.Number(default=1.0, bounds=(0.1, 10.0), doc='Scale factor')

    # String choices
    method = param.Selector(default='mean', objects=['mean', 'median', 'sum'])

    # Boolean flag
    normalize = param.Boolean(default=False)

    # List or array
    tags = param.List(default=[], item_type=str)
    data_array = param.Array(default=np.array([]))

# Instantiate and use
processor = DataProcessor()
print(f"Name: {processor.name}, Count: {processor.count}")

# Validate parameters automatically
processor.count = 500  # OK
processor.count = 2000  # Raises error: out of bounds

2. Advanced Parameter Types

class AdvancedConfig(param.Parameterized):
    # Date/time parameters
    date = param.Date(default='2024-01-01', doc='Start date')
    time = param.Time(default='12:00', doc='Start time')
    datetime = param.DateTime(default='2024-01-01 12:00:00')

    # File/path parameters
    input_file = param.Path(default=None, doc='Input file path')
    output_dir = param.Fspath(default='.', doc='Output directory')

    # Range parameter
    value_range = param.Range(default=(0, 10), bounds=(0, 100))

    # Color parameter
    color = param.Color(default='#FF0000')

    # JSON/Dict parameter
    config = param.Dict(default={}, per_instance=True)

    # DataFrame parameter
    dataframe = param.Parameter(default=None)

3. Dynamic Dependencies with @param.depends

class DataAnalyzer(param.Parameterized):
    data_source = param.Selector(
        default='random',
        objects=['random', 'sine', 'exponential']
    )
    amplitude = param.Number(default=1.0, bounds=(0.1, 10.0))
    frequency = param.Number(default=1.0, bounds=(0.1, 10.0))
    size = param.Integer(default=100, bounds=(10, 10000))

    @param.depends('data_source', 'amplitude', 'frequency', 'size')
    def get_data(self):
        """Automatically called when any dependency changes"""
        np.random.seed(42)
        x = np.linspace(0, 2*np.pi, self.size)

        if self.data_source == 'random':
            return x, np.random.randn(self.size) * self.amplitude
        elif self.data_source == 'sine':
            return x, self.amplitude * np.sin(self.frequency * x)
        else:  # exponential
            return x, self.amplitude * np.exp(self.frequency * x / 10)

    @param.depends('data_source', 'amplitude', 'frequency', 'size')
    def summary(self):
        """Display summary that updates automatically"""
        x, y = self.get_data()
        return f"Mean: {y.mean():.2f}, Std: {y.std():.2f}"

# Use in application
analyzer = DataAnalyzer()
print(analyzer.summary)

# Change parameters
analyzer.amplitude = 2.0
analyzer.frequency = 2.0
print(analyzer.summary)  # Updated automatically

4. Watchers for Side Effects

Watchers allow you to trigger code when parameters change:

class DataModel(param.Parameterized):
    filename = param.String(default='data.csv')
    data = param.Parameter(default=None, precedence=-1)

    @param.depends('filename', watch=True)
    def _load_data(self):
        """Automatically load data when filename changes"""
        print(f"Loading {self.filename}...")
        # Load file here
        self.data = pd.read_csv(self.filename)

    # Alternative: explicit watch
    def __init__(self, **params):
        super().__init__(**params)
        self.param.watch(self._on_count_change, 'count')

    def _on_count_change(self, event):
        print(f"Count changed from {event.old} to {event.new}")

model = DataModel()
model.filename = 'new_file.csv'  # Triggers _load_data automatically

5. Custom Validation

class ValidatedModel(param.Parameterized):
    email = param.String(default='', doc='Email address')
    age = param.Integer(default=0, bounds=(0, 150))
    password = param.String(default='')

    @param.validators('email')
    def validate_email(self, value):
        if '@' not in value:
            raise ValueError('Invalid email address')
        return value

    @param.validators('password')
    def validate_password(self, value):
        if len(value) < 8:
            raise ValueError('Password must be at least 8 characters')
        return value

    def validate_constraint(self):
        """Cross-parameter validation"""
        if self.age < 18 and self.email == 'restricted@example.com':
            raise ValueError('Minors cannot use this email')

model = ValidatedModel()
model.email = 'invalid'  # Raises ValueError
model.password = 'short'  # Raises ValueError

6. Hierarchical Parameterization

class DatabaseConfig(param.Parameterized):
    host = param.String(default='localhost')
    port = param.Integer(default=5432, bounds=(1, 65535))
    username = param.String(default='user')
    password = param.String(default='')

class AppConfig(param.Parameterized):
    app_name = param.String(default='MyApp')
    debug = param.Boolean(default=False)
    database = param.Parameter(default=DatabaseConfig())

    @param.depends('database.host', watch=True)
    def _on_db_change(self):
        print(f"Database configuration changed to {self.database.host}")

config = AppConfig()
config.database.host = 'production.db'  # Triggers watch on parent

Integration with Panel UI

1. Automatic UI Generation

import panel as pn

class DashboardConfig(param.Parameterized):
    title = param.String(default='Dashboard')
    refresh_interval = param.Integer(default=5000, bounds=(1000, 60000))
    metric = param.Selector(default='revenue', objects=['revenue', 'users', 'engagement'])
    show_legend = param.Boolean(default=True)

config = DashboardConfig()

# Panel automatically creates UI widgets from parameters
widgets = pn.param.ParamMethod.from_param(config.param)

# Or create individual widgets
title_input = pn.param.TextInput.from_param(config.param.title)
metric_select = pn.param.Selector.from_param(config.param.metric)
interval_slider = pn.param.IntSlider.from_param(config.param.refresh_interval)

2. Reactive Dashboard

import holoviews as hv

class InteractiveDashboard(param.Parameterized):
    metric = param.Selector(default='sales', objects=['sales', 'users', 'traffic'])
    time_range = param.Range(default=(0, 100), bounds=(0, 100))
    aggregation = param.Selector(default='daily', objects=['hourly', 'daily', 'weekly'])

    def __init__(self, data):
        super().__init__()
        self.data = data

    @param.depends('metric', 'time_range', 'aggregation')
    def plot(self):
        filtered = self.data[
            (self.data['metric'] == self.metric) &
            (self.data['value'] >= self.time_range[0]) &
            (self.data['value'] <= self.time_range[1])
        ]
        return filtered.hvplot.line(title=f'{self.metric} ({self.aggregation})')

    @param.depends('metric')
    def summary(self):
        subset = self.data[self.data['metric'] == self.metric]
        return f"Mean: {subset['value'].mean():.2f}"

dashboard = InteractiveDashboard(data_df)

pn.extension('material')
app = pn.Column(
    pn.param.ParamMethod.from_param(dashboard.param),
    pn.Column(dashboard.plot, dashboard.summary)
)

Best Practices

1. Parameter Organization

# Group related parameters
class VideoConfig(param.Parameterized):
    # Video inputs
    input_file = param.Path(doc='Input video file')
    start_frame = param.Integer(default=0, bounds=(0, None))
    end_frame = param.Integer(default=None, bounds=(0, None))

    # Processing
    scale = param.Number(default=1.0, bounds=(0.1, 2.0))
    quality = param.Selector(default='high', objects=['low', 'medium', 'high'])

    # Output
    output_format = param.Selector(default='mp4', objects=['mp4', 'webm', 'mov'])
    output_path = param.Path(default='output/')

2. Use Appropriate Parameter Types

# Bad: string for everything
config = param.Parameterized(
    count=param.String(default='10'),  # Wrong type
    flag=param.String(default='true')  # Wrong type
)

# Good: specific types with validation
class ProperConfig(param.Parameterized):
    count = param.Integer(default=10, bounds=(1, 100))
    flag = param.Boolean(default=True)

3. Leverage Watchers for Side Effects

class FileProcessor(param.Parameterized):
    input_path = param.Path(default=None)
    output_path = param.Path(default=None)

    # Use watch for file I/O and external effects
    @param.depends('input_path', watch=True)
    def _process_file(self):
        if self.input_path and self.input_path.exists():
            self._load_and_process()

    # Use depends for computation
    @param.depends('input_path')
    def file_size(self):
        if self.input_path:
            return self.input_path.stat().st_size
        return None

4. Documentation and Help

class WellDocumentedModel(param.Parameterized):
    threshold = param.Number(
        default=0.5,
        bounds=(0, 1),
        doc='Classification threshold. Values above this are classified as positive.',
        label='Classification Threshold',
        precedence=1  # Show first
    )

    @param.depends('threshold')
    def summary(self):
        """Return human-readable summary"""
        return f"Using threshold: {self.threshold}"

# Users can access help
help(WellDocumentedModel)
model = WellDocumentedModel()
print(model.param)  # Display all parameters with documentation

Common Patterns

Pattern 1: Configuration Object

class AppConfiguration(param.Parameterized):
    """Central configuration object for entire application"""
    debug = param.Boolean(default=False)
    log_level = param.Selector(default='INFO', objects=['DEBUG', 'INFO', 'WARNING', 'ERROR'])
    theme = param.Selector(default='light', objects=['light', 'dark'])

    @classmethod
    def from_file(cls, path):
        """Load configuration from file"""
        import json
        with open(path) as f:
            config = json.load(f)
        return cls(**config)

Pattern 2: Multi-Step Wizard

class Wizard(param.Parameterized):
    step = param.Integer(default=0, bounds=(0, 3))

    # Step 1: Data input
    data_source = param.Selector(default='file', objects=['file', 'database', 'api'])

    # Step 2: Processing
    algorithm = param.Selector(default='mean', objects=['mean', 'median'])

    # Step 3: Output
    format = param.Selector(default='csv', objects=['csv', 'json', 'parquet'])

    @param.depends('step')
    def current_step_view(self):
        if self.step == 0:
            return f"Select data source: {self.data_source}"
        elif self.step == 1:
            return f"Choose algorithm: {self.algorithm}"
        else:
            return f"Output format: {self.format}"

Pattern 3: Computed Parameters

class Statistics(param.Parameterized):
    values = param.Array(default=np.array([]))

    @property
    def mean(self):
        return np.mean(self.values)

    @property
    def std(self):
        return np.std(self.values)

    @param.depends('values')
    def summary(self):
        return f"Mean: {self.mean:.2f}, Std: {self.std:.2f}"

Integration with HoloViz Ecosystem

  • Panel: Auto-generate UIs from parameters
  • HoloViews: Create parameter-driven visualizations
  • hvPlot: Quick plots responding to parameter changes
  • Datashader: Efficient rendering with parameterized aggregation

Common Use Cases

  1. Configuration Management: Centralized app configuration
  2. Data Processing Pipelines: Parameterized workflows
  3. Scientific Simulations: Configurable model parameters
  4. Interactive Dashboards: Auto-generated control panels
  5. Machine Learning: Hyperparameter tuning interfaces
  6. Data Validation: Type-safe data entry

Troubleshooting

Issue: Watcher Not Triggering

  • Use watch=True on the @param.depends decorator
  • Ensure parameter actually changes
  • Check parameter name matches exactly

Issue: Circular Dependencies

  • Avoid watchers that modify parameters they depend on
  • Use separate input and derived parameters
  • Consider separating concerns into different objects

Issue: Performance with Expensive Computations

  • Cache results with @param.depends(..., cache_on=[])
  • Use explicit parameter changes rather than constant updates
  • Profile with profilers to find bottlenecks

Resources