{"record":{"id":"1b5de3ed59d35b79","repo":"apache/beam","slug":"zscore-score-one-expected-univariate-input-but-got-s","errorCode":null,"errorMessage":"ZScore.score_one expected univariate input, but got %s","messagePattern":"ZScore\\.score_one expected univariate input, but got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/ml/anomaly/detectors/zscore.py","lineNumber":109,"sourceCode":"    if len(x.__dict__) != 1:\n      raise ValueError(\n          \"ZScore.learn_one expected univariate input, but got %s\", str(x))\n\n    v = next(iter(x))\n    self._stdev_tracker.push(v)\n    self._sub_stat_tracker.push(v)\n\n  def score_one(self, x: beam.Row) -> Optional[float]:\n    \"\"\"Scores a data point using the Z-Score.\n\n    Args:\n      x: A `beam.Row` containing a single numerical value.\n\n    Returns:\n      float | None: The Z-Score.\n    \"\"\"\n    if len(x.__dict__) != 1:\n      raise ValueError(\n          \"ZScore.score_one expected univariate input, but got %s\", str(x))\n\n    v = next(iter(x))\n    if v is None or math.isnan(v):\n      return None\n\n    sub_stat = self._sub_stat_tracker.get()\n    stdev = self._stdev_tracker.get()\n\n    # not enough data points to compute sub_stat or standard deviation\n    if math.isnan(stdev) or math.isnan(sub_stat):\n      return float('NaN')\n\n    if abs(stdev) < EPSILON:\n      return 0.0\n\n    return abs((v - sub_stat) / stdev)\n","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/ml/anomaly/detectors/zscore.py#L91-L127","documentation":"ZScore.score_one computes the z-score of a single point and requires a univariate beam.Row with exactly one field; otherwise a ValueError is raised. Scoring shares the univariate constraint enforced at learning time.","triggerScenarios":"Scoring rows with multiple attributes (or zero) through ZScore.score_one, typically when the scored stream retains extra columns like keys or timestamps.","commonSituations":"Piping the original multi-column PCollection into both learn and score; enrichment steps adding fields between learn and score; accidental reuse of a generic row-mapping transform.","solutions":["Map to a single-field Row before score_one.","Keep the learn/score input shapes identical by reusing the same projection transform.","If multiple features must be scored together, instantiate one ZScore per feature."],"exampleFix":"// before\nwindowed | beam.Map(detector.score_one)\n// after\nwindowed | beam.Map(lambda r: beam.Row(value=r.metric)) | beam.Map(detector.score_one)","handlingStrategy":"validation","validationCode":"if len(row.__dict__) != 1:\n    raise ValueError(f\"ZScore.score_one requires exactly one field, got: {list(row.__dict__)}\")","typeGuard":"def is_univariate(row): return len(getattr(row, '__dict__', {})) == 1","tryCatchPattern":"try:\n    score = detector.score_one(row)\nexcept ValueError as e:\n    score = None\n    logger.warning(\"unscorable row %r: %s\", row, e)","preventionTips":["Re-project rows after any enrichment before scoring","Assert row arity in tests","Keep the learn/score shapes symmetric"],"tags":["python","anomaly-detection","validation","univariate"],"backgroundTag":"invalid-argument-value","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}