Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,17 @@ abstract class SklearnMLOperatorDescriptor[T <: ParamClass] extends PythonOperat
override def getOutputSchemas(
inputSchemas: Map[PortIdentity, Schema]
): Map[PortIdentity, Schema] = {
// The features go to `fit` as they are named, so the ground truth among them
// is the answer handed to the estimator as an input. Nothing fails when that
// happens, which is why it is refused here: the run finishes and the model
// scores far better than what it learned deserves.
if (Option(selectedFeatures).exists(_.contains(groundTruthAttribute))) {
throw new RuntimeException(
s""""$groundTruthAttribute" is the Ground Truth Attribute Column, so it cannot""" +
" also be a Selected Feature. Remove it from Selected Features, or fit against" +
" a different column."
)
}
val outputSchema = Schema(
List(
new Attribute("Model", AttributeType.BINARY),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,16 @@ abstract class SklearnModelOpDesc extends PythonOperatorDescriptor {
s" produces. Turn Count Vectorizer off, or use $alternatives."
)
}
// The generated code drops the target before the text pipeline reads its
// columns, so naming it here asks the pipeline for a column that is no
// longer there. Refused rather than vectorized: the label is the answer,
// and a model given it as a feature reads that answer off its own input.
if (text.contains(target)) {
throw new RuntimeException(
s""""$target" is the Target Attribute, so it cannot also be a Text Attribute.""" +
" Remove it from Text Attribute, or fit against a different column."
)
}
}
Map(
operatorInfo.outputPorts.head.id -> Schema()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,23 @@ class SklearnMLOperatorDescriptorSpec extends AnyFlatSpec with Matchers {
)
}

it should "reject the ground truth column named as a feature" in {
val op = newOp()
op.groundTruthAttribute = "species"
op.selectedFeatures = List("petal_length", "species")
val thrown = intercept[RuntimeException](op.getOutputSchemas(Map.empty))
thrown.getMessage should include("species")
thrown.getMessage should include("Ground Truth Attribute Column")
thrown.getMessage should include("Selected Features")
}

it should "let the features through while none of them is the ground truth" in {
val op = newOp()
op.groundTruthAttribute = "species"
op.selectedFeatures = List("petal_length", "petal_width")
op.getOutputSchemas(Map.empty).keySet shouldBe Set(op.operatorInfo.outputPorts.head.id)
}

"SklearnMLOperatorDescriptor" should
"default paraList to empty, groundTruthAttribute to empty, and selectedFeatures to null" in {
val op = newOp()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,32 @@ class SklearnModelOpDescSpec extends AnyFlatSpec with Matchers {
}
d.getOutputSchemas(Map.empty).keySet shouldBe Set(d.operatorInfo.outputPorts.head.id)
}

it should "reject the target named as a text column" in {
val d = new TestSklearnModelOpDesc
d.countVectorizer = true
d.target = "species"
d.text = List("note", "species")
val thrown = intercept[RuntimeException](d.getOutputSchemas(Map.empty))
thrown.getMessage should include("species")
thrown.getMessage should include("Target Attribute")
thrown.getMessage should include("Text Attribute")
}

it should "let the text columns through while none of them is the target" in {
val d = new TestSklearnModelOpDesc
d.countVectorizer = true
d.target = "species"
d.text = List("note")
d.getOutputSchemas(Map.empty).keySet shouldBe Set(d.operatorInfo.outputPorts.head.id)
}

it should "leave a stale text column alone while Count Vectorizer is off" in {
// Nothing reads `text` with the switch off, and the panel hides it, so a value
// left behind by an earlier configuration must not report the operator invalid.
val d = new TestSklearnModelOpDesc
d.target = "species"
d.text = List("species")
d.getOutputSchemas(Map.empty).keySet shouldBe Set(d.operatorInfo.outputPorts.head.id)
}
}
Loading