Helper Functions Guide¶
Pollywog provides a collection of helper functions to simplify common calculation patterns. These functions can generate either complete Number or Category objects for your CalcSet, or return just the expression/logic for use in more complex calculations.
Why Use Helpers?¶
Helper functions offer several advantages:
Readability:
Sum(["Au_assay1", "Au_assay2", "Au_assay3"])is clearer than manually writing the expressionConsistency: Helpers ensure correct syntax and parentheses
Error Prevention: Reduce mistakes in complex expressions
Maintainability: Easier to update and refactor calculations
Self-Documentation: Helper names clearly indicate intent
Flexibility: Can return either complete calculations or just expressions for composition
All helper functions are available from the pollywog.helpers module:
from pollywog.helpers import (
Sum, Product, Average, WeightedSum, WeightedAverage,
Scale, Normalize, NumberFromThresholds, CategoryFromThresholds
)
How Helpers Work: Two Modes¶
All helper functions have two modes of operation:
With ``name`` parameter: Returns a complete
NumberorCategoryobject ready to add to aCalcSetWithout ``name`` parameter: Returns just the expression (string) or logic (If block) for use in more complex calculations
This flexibility allows you to use helpers both for simple standalone calculations and as building blocks for more complex logic.
from pollywog.helpers import Sum, Product
from pollywog.core import Number
# Mode 1: Complete calculation (with name)
# Example: Average of multiple assay results
avg_Au = Sum(["Au_lab1", "Au_lab2", "Au_lab3"], name="Au_total_for_avg")
# Returns a Number object ready for CalcSet
# Mode 2: Expression only (without name)
# Example: Calculate block volume
volume_expr = Product(["block_x", "block_y", "block_z"])
# Returns: "([block_x] * [block_y] * [block_z])"
# Use this in more complex expressions:
tonnage_calc = Number(
name="tonnage",
expression=[f"{volume_expr} * [density]"]
)
When to use each mode:
With name: Simple, standalone calculations
Without name: Building blocks for complex logic, nested expressions, or conditional calculations
Mathematical Helpers¶
Sum¶
Add multiple variables together.
from pollywog.helpers import Sum
# With name: Returns a Number
total = Sum(["Au_assay1", "Au_assay2", "Au_assay3"], name="Au_composite")
# Generates: Number(name="Au_composite", expression=["([Au_assay1] + [Au_assay2] + [Au_assay3])"])
# Without name: Returns expression string
sum_expr = Sum(["length", "width", "height"])
# Returns: "([length] + [width] + [height])"
# Use expression in complex logic
from pollywog.core import Number
value_calc = Number(
name="total_dimension",
expression=[f"{sum_expr} * [scale_factor]"]
)
Parameters:
variables: List of variable names as stringsname(optional): Name for output variable. If None, returns expression stringcomment(optional): Custom comment for the calculation
Returns: Number object when name is provided, or expression string when name is None
Product¶
Multiply multiple variables together.
from pollywog.helpers import Product
# With name: Returns a Number
payable = Product(["grade", "recovery", "tonnes"], name="payable_metal")
# Generates: Number(name="payable_metal", expression=["([grade] * [recovery] * [tonnes])"])
# Without name: Returns expression string
prod_expr = Product(["grade", "recovery"])
# Returns: "([grade] * [recovery])"
# Use in complex calculation
from pollywog.core import Number
metal_value = Number(
name="metal_value_usd",
expression=[f"{prod_expr} * [tonnes] * [price_per_oz]"]
)
Parameters:
variables: List of variable names as stringsname(optional): Name for output variable. If None, returns expression stringcomment(optional): Custom comment for the calculation
Returns: Number object when name is provided, or expression string when name is None
Average¶
Calculate the arithmetic mean of multiple variables.
from pollywog.helpers import Average
# With name: Returns a Number
avg_grade = Average(["Au_kriging", "Au_idw", "Au_nn"], name="Au_average")
# Generates: ([Au_kriging] + [Au_idw] + [Au_nn]) / 3
# Without name: Returns expression string for reuse
avg_expr = Average(["est1", "est2", "est3"])
# Returns: "([est1] + [est2] + [est3]) / 3"
Parameters:
variables: List of variable names as stringsname(optional): Name for output variable. If None, returns expression stringcomment(optional): Custom comment for the calculation
Returns: Number object when name is provided, or expression string when name is None
WeightedAverage¶
Calculate a weighted average of variables. Essential for domain proportion calculations.
from pollywog.helpers import WeightedAverage
# Weighted by domain proportions
weighted_grade = WeightedAverage(
variables=["Au_oxide", "Au_sulfide", "Au_transition"],
weights=["prop_oxide", "prop_sulfide", "prop_transition"],
name="Au_composite"
)
# Generates: ([Au_oxide] * [prop_oxide] + [Au_sulfide] * [prop_sulfide] +
# [Au_transition] * [prop_transition]) /
# ([prop_oxide] + [prop_sulfide] + [prop_transition])
# With constant weights
method_blend = WeightedAverage(
variables=["Au_kriging", "Au_idw"],
weights=[0.7, 0.3],
name="Au_blended"
)
# Generates: ([Au_kriging] * 0.7 + [Au_idw] * 0.3) / (0.7 + 0.3)
Parameters:
variables: List of variable names to averageweights: List of weights (can be variable names or numeric constants)name(optional): Name for output variable (default:wavg_<var1>_<var2>_...)comment(optional): Custom comment for the calculation
Returns: Number object
Important: Weights are automatically summed in the denominator, so they don’t need to sum to 1.
WeightedSum¶
Calculate a weighted sum of variables (without dividing by the sum of weights).
from pollywog.helpers import WeightedSum
# Weighted contribution from multiple metals
metal_value = WeightedSum(
variables=["Au_composite", "Ag_composite"],
weights=[1800, 22],
name="metal_value_per_tonne"
)
# Generates: ([Au_composite] * 1800 + [Ag_composite] * 22)
# Without name: Returns expression string
ws_expr = WeightedSum(["Au", "Ag"], [0.7, 0.3])
# Returns: "([Au] * 0.7 + [Ag] * 0.3)"
Parameters:
variables: List of variable names to sumweights: List of weights (can be variable names or numeric constants)name(optional): Name for output variable. If None, returns expression stringcomment(optional): Custom comment for the calculation
Returns: Number object when name is provided, or expression string when name is None
Note: Unlike WeightedAverage, this does not divide by the sum of weights. Use this when you want the raw weighted total (e.g., revenue calculations).
Scale¶
Multiply a variable by a scaling factor.
from pollywog.helpers import Scale
# Apply dilution factor
diluted = Scale("Au_est", 0.95, name="Au_diluted")
# Generates: [Au_est] * 0.95
# Scale by another variable
adjusted = Scale("grade", "adjustment_factor", name="grade_adjusted")
# Generates: [grade] * [adjustment_factor]
Parameters:
variable: Variable name to scalefactor: Scaling factor (number or variable name)name(optional): Name for output variable (default:scale_<variable>)comment(optional): Custom comment for the calculation
Returns: Number object
Normalize¶
Normalize a variable to the range [0, 1] given min and max values.
from pollywog.helpers import Normalize
# Normalize grade to 0-1 range
norm_grade = Normalize("Au", min_value=0, max_value=10, name="Au_normalized")
# Generates: ([Au] - 0) / (10 - 0)
# Normalize depth
norm_depth = Normalize("depth", min_value=0, max_value=500, name="depth_norm")
Parameters:
variable: Variable name to normalizemin_value: Minimum value for normalizationmax_value: Maximum value for normalizationname(optional): Name for output variable (default:norm_<variable>)comment(optional): Custom comment for the calculation
Returns: Number object
Note: If the variable equals min_value, result is 0. If it equals max_value, result is 1.
Classification Helpers¶
CategoryFromThresholds¶
Create categorical classifications based on numeric thresholds. Perfect for grade shells, ore types, and quality classifications.
from pollywog.helpers import CategoryFromThresholds
# Classify ore grade
ore_class = CategoryFromThresholds(
variable="Au",
thresholds=[0.3, 1.0, 3.0],
categories=["waste", "low_grade", "medium_grade", "high_grade"],
name="Au_class"
)
# Creates an If statement:
# If Au <= 0.3: "waste"
# Else if Au <= 1.0: "low_grade"
# Else if Au <= 3.0: "medium_grade"
# Else: "high_grade"
# Rock quality designation
rqd_class = CategoryFromThresholds(
variable="RQD",
thresholds=[25, 50, 75, 90],
categories=["very_poor", "poor", "fair", "good", "excellent"],
name="rock_quality",
comment="RQD classification per Deere, 1989"
)
Parameters:
variable: Variable to thresholdthresholds: List of threshold values (must be sorted in ascending order)categories: List of category labels (length must belen(thresholds) + 1)name(optional): Name for output category (default:class_<variable>)comment(optional): Custom comment for the calculation
Returns: Category object
Important: - Thresholds must be in ascending order - Number of categories must be one more than number of thresholds - Categories below or equal to first threshold get first label - Categories above last threshold get last label
NumberFromThresholds¶
Create numeric value assignments based on thresholds. The numeric counterpart to CategoryFromThresholds — assigns numbers instead of category labels.
from pollywog.helpers import NumberFromThresholds
# Assign numeric scores based on grade ranges
grade_score = NumberFromThresholds(
variable="Au",
thresholds=[0.5, 1.0, 2.0],
values=[0, 1, 2, 3],
name="Au_score"
)
# Creates an If statement:
# If Au <= 0.5: 0
# Else if Au <= 1.0: 1
# Else if Au <= 2.0: 2
# Else: 3
# Recovery factors by grade range
recovery = NumberFromThresholds(
variable="Au",
thresholds=[0.5, 2.0, 5.0],
values=[0.75, 0.82, 0.88, 0.92],
name="Au_recovery",
comment="Grade-dependent recovery factor"
)
# Without name: Returns If block for composition
score_if = NumberFromThresholds("RQD", [25, 50, 75], [1, 2, 3, 4])
# Returns an If object for use in more complex expressions
Parameters:
variable: Variable to thresholdthresholds: List of threshold values (must be sorted in ascending order)values: List of numeric values (length must belen(thresholds) + 1)name(optional): Name for output variable. If None, returns the If blockcomment(optional): Custom comment for the calculation
Returns: Number object when name is provided, or If block when name is None
Important: - Thresholds must be in ascending order - Number of values must be one more than number of thresholds - Values below or equal to first threshold get first value - Values above last threshold get last value
Using Helpers in Workflows¶
Combining Multiple Helpers¶
Helpers work well together to build complex calculations:
from pollywog.core import CalcSet
from pollywog.helpers import WeightedAverage, Product, CategoryFromThresholds
# Multi-metal resource estimation
domains = ["oxide", "transition", "sulfide"]
calcset = CalcSet([
# Weighted grades by domain
WeightedAverage(
variables=[f"Au_{d}" for d in domains],
weights=[f"prop_{d}" for d in domains],
name="Au_composite"
),
WeightedAverage(
variables=[f"Cu_{d}" for d in domains],
weights=[f"prop_{d}" for d in domains],
name="Cu_composite"
),
# Payable metal
Product("Au_composite", "recovery_Au", "tonnes", name="Au_payable_oz"),
Product("Cu_composite", "recovery_Cu", "tonnes", name="Cu_payable_lbs"),
# Classification
CategoryFromThresholds(
variable="Au_composite",
thresholds=[0.5, 1.5],
categories=["low", "medium", "high"],
name="grade_class"
),
])
calcset.to_lfcalc("multi_metal_workflow.lfcalc")
Programmatic Generation¶
Use Python loops to generate many similar calculations:
from pollywog.core import CalcSet
from pollywog.helpers import WeightedAverage, Scale
metals = ["Au", "Ag", "Cu", "Pb", "Zn"]
domains = ["high_grade", "medium_grade", "low_grade"]
calculations = []
# Weighted average for each metal
for metal in metals:
calculations.append(
WeightedAverage(
variables=[f"{metal}_{d}" for d in domains],
weights=[f"prop_{d}" for d in domains],
name=f"{metal}_composite",
comment=f"Domain-weighted {metal} grade"
)
)
# Apply recovery factor to each metal
recovery_factors = {
"Au": 0.88, "Ag": 0.75, "Cu": 0.85, "Pb": 0.80, "Zn": 0.82
}
for metal in metals:
calculations.append(
Scale(
f"{metal}_composite",
recovery_factors[metal],
name=f"{metal}_recovered",
comment=f"{metal} grade after {recovery_factors[metal]*100:.0f}% recovery"
)
)
# Create and export calcset
calcset = CalcSet(calculations)
calcset.to_lfcalc("automated_workflow.lfcalc")
When to Use Helpers vs. Manual Expressions¶
Use Helpers When:
The calculation matches a helper pattern (sum, average, product, etc.)
You want readable, self-documenting code
You’re generating many similar calculations programmatically
You want to minimize syntax errors
Use Manual Expressions When:
The calculation is unique or complex
You need fine control over the expression
The calculation doesn’t fit any helper pattern
You need to use specific Leapfrog functions (clamp, log, etc.)
Example comparing both approaches:
from pollywog.core import CalcSet, Number
from pollywog.helpers import Sum, Product
# Using helpers with name (returns Number)
helper_approach = CalcSet([
# Sum multiple Au assays for composite
Sum(["Au_north", "Au_south", "Au_center"], name="Au_total"),
# Calculate block tonnage
Product(["block_volume", "density"], name="tonnage"),
])
# Using manual expressions (needed for complex logic)
manual_approach = CalcSet([
Number(name="complex_calc", expression=[
"log([Au] + 0.01) * clamp([recovery], 0.5, 1.0) + [base_adjustment]"
]),
])
# Combining helpers without name for complex expressions
from pollywog.helpers import WeightedAverage, Product
mixed_approach = CalcSet([
# Simple standalone: average multiple Au estimates
Sum(["Au_kriging", "Au_idw", "Au_nn"], name="Au_sum_for_avg"),
# Complex: Calculate NSR using helper composition
Number(
name="NSR_Au",
expression=[
# Gold revenue: grade * price * recovery * conversion
f"{Product(['Au_composite', '31.1035'])} * [Au_price_per_oz] * [Au_recovery]"
]
),
])
Advanced Usage: Composing Helpers¶
The real power of helpers returning expressions (when name=None) is the ability to compose them into more complex calculations.
Nested Helper Expressions¶
from pollywog.core import Number, CalcSet
from pollywog.helpers import Sum, Product, Average, WeightedAverage
# Build complex expression from helper building blocks
calcset = CalcSet([
# Calculate total block dimensions for geometric checks
Number(
name="total_block_dimension",
expression=[
f"({Sum(['block_x', 'block_y', 'block_z'])})"
]
),
# Average of multiple Au estimates times tonnage
Number(
name="Au_metal_content",
expression=[
f"{Average(['Au_kriging', 'Au_idw', 'Au_nn'])} * "
f"{Product(['block_volume', 'density'])}"
]
),
# Weighted average grade with clamping for safety
Number(
name="Au_composite_safe",
expression=[
f"clamp({WeightedAverage(['Au_oxide', 'Au_sulfide'], ['prop_oxide', 'prop_sulfide'])}, 0, 100)"
]
),
])
Using Helper Expressions in Conditional Logic¶
from pollywog.core import Number, If, CalcSet
from pollywog.helpers import Product, CategoryFromThresholds
# Use helper expressions in If conditions
# Example: Adjust dilution based on Au grade
calcset = CalcSet([
Number(
name="dilution_factor",
expression=[
If([
("[Au_composite] > 5", "1.05"), # High grade: 5% dilution
("[Au_composite] > 2", "1.10"), # Medium grade: 10% dilution
], otherwise=["1.15"]) # Low grade: 15% dilution
]
),
# Calculate metal content with grade-dependent recovery
Number(
name="Au_recovered_oz",
expression=[
If([
("[Au_composite] > 3",
f"{Product(['Au_composite', '31.1035', 'tonnage', '0.92'])}"), # High grade: 92% recovery
("[Au_composite] > 1",
f"{Product(['Au_composite', '31.1035', 'tonnage', '0.88'])}"), # Medium: 88% recovery
], otherwise=[f"{Product(['Au_composite', '31.1035', 'tonnage', '0.82'])}"]) # Low: 82% recovery
]
),
])
Real-World Example: Multi-Metal Resource Model¶
from pollywog.core import CalcSet, Number
from pollywog.helpers import WeightedAverage, Sum, Product, Scale
metals = ["Au", "Ag", "Cu", "Pb", "Zn"]
domains = ["oxide", "transition", "sulfide"]
calculations = []
# Step 1: Domain-weighted composites (with name)
for metal in metals:
calculations.append(
WeightedAverage(
variables=[f"{metal}_{d}" for d in domains],
weights=[f"prop_{d}" for d in domains],
name=f"{metal}_composite"
)
)
# Step 2: Revenue calculation with nested helpers - each metal priced individually
prices = {"Au": 1800, "Ag": 22, "Cu": 3.5, "Pb": 0.9, "Zn": 1.2}
recoveries = {"Au": 0.88, "Ag": 0.75, "Cu": 0.85, "Pb": 0.80, "Zn": 0.82}
revenue_terms = []
for metal in metals:
# Use Scale without name to get expression
recovered_expr = Scale(f"{metal}_composite", recoveries[metal])
# Use Product without name to get expression
value_expr = Product([recovered_expr, str(prices[metal])])
revenue_terms.append(value_expr)
# Sum all revenue terms
calculations.append(
Number(
name="total_revenue_per_tonne",
expression=[Sum(revenue_terms)],
comment_equation="Total revenue from all metals after recovery"
)
)
calcset = CalcSet(calculations)
calcset.to_lfcalc("complex_resource_model.lfcalc")
- This example shows how helpers with
name=Noneenable building complex calculations while keeping code readable and maintainable. “[precious] * clamp([price_ratio], 0, 5) + [premium]”
]),
])
Custom Helper Functions¶
You can create your own helper functions following the same pattern:
from pollywog.core import Number
def GradeThickness(grade_var, thickness_var, name=None, comment=None):
"""
Calculate grade-thickness product (common in vein modeling).
"""
if name is None:
name = f"GT_{grade_var}"
expr = f"[{grade_var}] * [{thickness_var}]"
return Number(
name,
[expr],
comment_equation=comment or f"Grade-thickness product for {grade_var}"
)
def MetalValue(grade_var, price, recovery=1.0, name=None, comment=None):
"""
Calculate metal value per tonne.
"""
if name is None:
name = f"value_{grade_var}"
expr = f"[{grade_var}] * {price} * {recovery} / 31.1035"
return Number(
name,
[expr],
comment_equation=comment or f"Metal value for {grade_var} at ${price}/oz"
)
# Use your custom helpers
from pollywog.core import CalcSet
calcset = CalcSet([
GradeThickness("Au", "vein_width", name="Au_GT"),
MetalValue("Au", 1800, recovery=0.88, name="Au_value_per_tonne"),
])
Helper Function Reference¶
Quick reference table:
Helper |
Returns |
Use Case |
|---|---|---|
Sum |
Number |
Add multiple variables |
Product |
Number |
Multiply multiple variables |
Average |
Number |
Arithmetic mean |
WeightedSum |
Number |
Weighted total (revenue, scoring) |
WeightedAverage |
Number |
Weighted mean (domain proportions) |
Scale |
Number |
Multiply by factor (dilution, recovery) |
Normalize |
Number |
Scale to [0, 1] range |
NumberFromThresholds |
Number |
Assign numeric values by thresholds |
CategoryFromThresholds |
Category |
Classify by numeric thresholds |
Common Patterns¶
Domain Proportion Weighting¶
from pollywog.helpers import WeightedAverage
domains = ["dom1", "dom2", "dom3"]
metals = ["Au", "Ag", "Cu"]
calcs = [
WeightedAverage(
variables=[f"{metal}_{d}" for d in domains],
weights=[f"prop_{d}" for d in domains],
name=f"{metal}_final"
)
for metal in metals
]
Multi-Step Calculations¶
from pollywog.core import CalcSet
from pollywog.helpers import Product, Scale
CalcSet([
# Step 1: Apply dilution
Scale("Au_est", 0.95, name="Au_diluted"),
# Step 2: Apply recovery
Scale("Au_diluted", 0.88, name="Au_recovered"),
# Step 3: Calculate metal content
Product("Au_recovered", "tonnes", name="Au_ounces_total"),
])
Economic Calculations¶
from pollywog.core import CalcSet, Number
from pollywog.helpers import Sum, Product
CalcSet([
# Revenue per tonne for each metal
Product("Au_recovered", "Au_price", name="Au_revenue_per_oz"),
Product("Cu_recovered", "Cu_price", name="Cu_revenue_per_lb"),
# Convert to $/tonne
Number(name="Au_value", expression=["[Au_revenue_per_oz] / 31.1035"]),
Number(name="Cu_value", expression=["[Cu_revenue_per_lb] * 22.046"]),
# Total revenue
Sum("Au_value", "Cu_value", name="total_revenue_per_tonne"),
])
See Also¶
Leapfrog Expression Syntax Guide - Full expression syntax reference
Common Workflow Patterns - Common workflow examples
API Reference - Complete API documentation
Tutorials - Step-by-step tutorials