Files
2026-07-13 13:22:34 +08:00

89 lines
3.3 KiB
Python

import math
from dataclasses import dataclass
from mlflow.entities._mlflow_object import _MlflowObject
from mlflow.protos.service_pb2 import CalculateTraceFilterCorrelation
@dataclass
class TraceFilterCorrelationResult(_MlflowObject):
"""
Result of calculating correlation between two trace filter conditions.
This class represents the correlation analysis between two trace filters,
using Normalized Pointwise Mutual Information (NPMI) as the correlation metric.
NPMI ranges from -1 to 1:
- -1: Perfect negative correlation (filters never co-occur)
- 0: Independence (filters occur independently)
- 1: Perfect positive correlation (filters always co-occur)
- NaN: Undefined (when one or both filters have zero matches)
Attributes:
npmi: Normalized Pointwise Mutual Information score (unsmoothed).
Returns NaN when undefined (e.g., when filter1_count=0 or filter2_count=0).
Returns -1.0 when filters never co-occur but both have support.
Otherwise returns a value in [-1, 1].
npmi_smoothed: NPMI calculated with Jeffreys prior smoothing (alpha=0.5).
More robust for small sample sizes and confidence interval estimation.
Returns NaN when undefined.
filter1_count: Number of traces matching the first filter.
filter2_count: Number of traces matching the second filter.
joint_count: Number of traces matching both filters.
total_count: Total number of traces in the experiment(s).
confidence_lower: Lower bound of the confidence interval for NPMI (optional).
confidence_upper: Upper bound of the confidence interval for NPMI (optional).
"""
npmi: float
filter1_count: int
filter2_count: int
joint_count: int
total_count: int
npmi_smoothed: float | None = None
confidence_lower: float | None = None
confidence_upper: float | None = None
@classmethod
def from_proto(cls, proto):
"""
Create a TraceFilterCorrelationResult from a protobuf response.
Args:
proto: CalculateTraceFilterCorrelation.Response protobuf message
Returns:
TraceFilterCorrelationResult instance
"""
return cls(
npmi=proto.npmi if proto.HasField("npmi") else float("nan"),
npmi_smoothed=proto.npmi_smoothed if proto.HasField("npmi_smoothed") else None,
filter1_count=proto.filter1_count,
filter2_count=proto.filter2_count,
joint_count=proto.joint_count,
total_count=proto.total_count,
)
def to_proto(self):
"""
Convert this result to a protobuf response message.
Returns:
CalculateTraceFilterCorrelation.Response protobuf message
"""
response = CalculateTraceFilterCorrelation.Response()
if self.npmi is not None and not math.isnan(self.npmi):
response.npmi = self.npmi
if self.npmi_smoothed is not None and not math.isnan(self.npmi_smoothed):
response.npmi_smoothed = self.npmi_smoothed
response.filter1_count = self.filter1_count
response.filter2_count = self.filter2_count
response.joint_count = self.joint_count
response.total_count = self.total_count
return response