Adding a Measure
Besides the built-in measures, you can run your own measure by
passing a function to measure_diversity(); no need to modify the package.
If you would like to contribute a embedding-based diversity measure to the package,
see Adding New Measures
in the README — it also covers tagging the measure with its family from the
taxonomy.
The contract
A custom measure needs to be a function with a signature exactly like a built-in measure: it receives the
data you passed to measure_diversity() plus the embedding_model key, and a diversity_axis key. It should return a
{"value": float, "parameters": {...}} dict (the MeasureResult type). Make
resolve_embeddings its first line (it returns a numpy array and a string for the embedding model name used if any).
The resolve_embeddings function is the single place input is validated and
text is embedded:
from emb_diversity.embed import resolve_embeddings
from emb_diversity.measures.types import MeasureResult
def custom_measure(data, *, diversity_axis="semantic", embedding_model=None) -> MeasureResult:
"""A custom measure: the standard deviation of the vectors."""
vectors, model = resolve_embeddings(
data, diversity_axis, embedding_model, measure="custom_measure"
)
return {"value": float(vectors.std()), "parameters": {"embedding_model": model}}
data— what you pass tomeasure_diversity(): a list of text strings, or an(n, d)array of vectors.resolve_embeddings— the same helper the built-ins use. It validates the input (rejecting a bare string, non-2-D data, fewer than 2 samples, nan/inf), embeds text, and returns the vectors plus the resolved embedding-model id (Nonefor vector input).measure="custom_measure"— optional. When given, an interactive “Calculating measure ‘custom_measure’…” notice is printed once embedding finishes, just before your calculation runs (shown only in interactive sessions). Leave it out and the measure works the same, just without the notice.return a dict with a float
"value"and a"parameters"dict recording the configuration used.
If you want your custom measure to support long-text chunking (passed as
measure_diversity(..., chunking_kwargs={...})), accept a chunking_kwargs
argument and forward it to resolve_embeddings:
def custom_measure(data, *, diversity_axis="semantic", embedding_model=None,
chunking_kwargs=None) -> MeasureResult:
vectors, model = resolve_embeddings(
data, diversity_axis, embedding_model, measure="custom_measure",
chunking_kwargs=chunking_kwargs,
)
return {"value": float(vectors.std()), "parameters": {"embedding_model": model}}
measure_diversity only forwards chunking_kwargs when the caller sets it, so a
custom measure without this argument still works for the default (truncation) path.
Reusing built-in helpers
For distance-based measures you can reuse compute_pairwise_distances — the same
cached helper the built-ins use. It returns the condensed array of all pairwise
distances (like scipy.spatial.distance.pdist) and shares the on-disk distance
cache, so repeated runs over the same vectors and measures are cheap:
from emb_diversity import compute_pairwise_distances
from emb_diversity.embed import resolve_embeddings
from emb_diversity.measures.types import MeasureResult
def my_min_dist(data, metric="cosine", *, diversity_axis="semantic", embedding_model=None) -> MeasureResult:
"""A custom measure: the smallest pairwise distance."""
vectors, model = resolve_embeddings(
data, diversity_axis, embedding_model, measure="my_min_dist"
)
dists = compute_pairwise_distances(vectors, metric)
return {
"value": float(dists.min()),
"parameters": {"metric": metric, "embedding_model": model},
}
Running it
Pass the function as measure. The data you give measure_diversity() is handed
to your measure unchanged. It can be a list of text strings (embedded for you) or
an (n, d) array of vectors, exactly like the built-in measures:
import numpy as np
from emb_diversity import measure_diversity
# Text input — embedded via the diversity axis / embedding model.
texts = ["The cat sat on the mat.", "Dogs play fetch.", "A bird sings at dawn."]
measure_diversity(texts, measure=custom_measure)
# {'custom_measure': {'value': ..., 'parameters': {'embedding_model': '...'}}}
# Vector input — used directly; no embedding, so embedding_model is None.
vectors = np.random.randn(100, 384)
measure_diversity(vectors, measure=custom_measure)
# {'custom_measure': {'value': ..., 'parameters': {'embedding_model': None}}}
# Mixed with built-in measures, in a list:
measure_diversity(texts, measure=["mean_pw_dist", custom_measure])
# {'mean_pw_dist': {...}, 'custom_measure': {...}}
Your measure is keyed by its function name (custom_measure) in the result.
Good to know
Run as given. A custom measure is not wrapped or checked — it runs exactly as written. Calling
resolve_embeddingsfirst (as above) is what validates the input, so make it the first line of your measure.Failures are isolated. If your measure raises, its
"value"isnan, the entry gains an"error"key, and aUserWarningis emitted — the other measures in the call still run. This matches how built-in failures are reported.Embeddings are computed once. When
datais text,resolve_embeddingsreuses the on-disk embedding cache, so the model runs only once across every measure in ameasure_diversity()call — including your own.