{"record":{"id":"ca85cdd31c8d51f2","repo":"apache/beam","slug":"iqr-learn-one-expected-univariate-input-but-got-s-str-x","errorCode":null,"errorMessage":"\"IQR.learn_one expected univariate input, but got %s\", str(x)","messagePattern":"\"IQR\\.learn_one expected univariate input, but got (.+?)\", str\\(x\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/ml/anomaly/detectors/iqr.py","lineNumber":87,"sourceCode":"\n    self._q1_tracker = q1_tracker or \\\n        BufferedSlidingQuantileTracker(DEFAULT_WINDOW_SIZE, 0.25)\n    assert self._q1_tracker._q == 0.25, \\\n        \"q1_tracker must be initialized with q = 0.25\"\n\n    self._q3_tracker = q3_tracker or \\\n        SecondaryBufferedQuantileTracker(self._q1_tracker, 0.75)\n    assert self._q3_tracker._q == 0.75, \\\n        \"q3_tracker must be initialized with q = 0.75\"\n\n  def learn_one(self, x: beam.Row) -> None:\n    \"\"\"Updates the quantile trackers with a new data point.\n\n    Args:\n      x: A `beam.Row` containing a single numerical value.\n    \"\"\"\n    if len(x.__dict__) != 1:\n      raise ValueError(\n          \"IQR.learn_one expected univariate input, but got %s\", str(x))\n\n    v = next(iter(x))\n    self._q1_tracker.push(v)\n    self._q3_tracker.push(v)\n\n  def score_one(self, x: beam.Row) -> Optional[float]:\n    \"\"\"Scores a data point based on its deviation from the IQR.\n\n    Args:\n      x: A `beam.Row` containing a single numerical value.\n\n    Returns:\n      float | None: The anomaly score.\n    \"\"\"\n    if len(x.__dict__) != 1:\n      raise ValueError(\n          \"IQR.score_one expected univariate input, but got %s\", str(x))","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/ml/anomaly/detectors/iqr.py#L69-L105","documentation":"IQR.learn_one updates the Q1/Q3 quantile trackers and expects x to be a beam.Row with exactly one field (univariate). If the row has zero or multiple fields, a ValueError is raised. This guards the IQR detector, which mathematically only operates on a single value per data point.","triggerScenarios":"Passing a beam.Row with more than one attribute (multivariate input) or an empty row to IQR.learn_one, e.g. beam.Row(value=..., timestamp=...) fed directly into the detector's learn step.","commonSituations":"Users convert a multi-column PCollection into a Row without projecting down to one numeric column; a pipeline schema change adds a second field to the Row; accidentally passing a named tuple or dict-backed Row with metadata fields.","solutions":["Project the input to a single numeric field, e.g. rows | beam.Map(lambda r: beam.Row(value=r.value)).","Split multi-column data into separate detector pipelines, one per metric.","Use a multivariate-capable detector if the input is intentionally multi-dimensional."],"exampleFix":"# before\npc | anomaly.IQR(overrides).learn_one()  # Row(value=1.0, label='x')\n# after\npc | beam.Map(lambda r: beam.Row(value=r.value)) | anomaly.IQR(overrides).learn_one()","handlingStrategy":"validation","validationCode":"if len(row.__dict__) != 1:\n    raise ValueError(f\"IQR.learn_one requires exactly one field, got: {list(row.__dict__)}\")","typeGuard":"def is_univariate(row): return len(getattr(row, '__dict__', {})) == 1","tryCatchPattern":"try:\n    detector.learn_one(row)\nexcept ValueError as e:\n    logger.warning(\"skipping non-univariate row %r: %s\", row, e)","preventionTips":["Always project multi-column PCollections to a single-field beam.Row before detectors","Keep learn/score shaping in one shared transform","Add schema assertions after any join or enrichment step"],"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"}