mirror of
https://github.com/google/earthengine-api.git
synced 2025-12-08 19:26:12 +00:00
593 lines
19 KiB
Python
593 lines
19 KiB
Python
#!/usr/bin/env python3
|
|
"""Tests for the ee.Classifier module."""
|
|
|
|
import json
|
|
import sys
|
|
from typing import Any
|
|
import unittest
|
|
|
|
import unittest
|
|
import ee
|
|
from ee import apitestcase
|
|
|
|
|
|
_CLASSIFIER_JSON = {
|
|
'result': '0',
|
|
'values': {
|
|
'0': {
|
|
'functionInvocationValue': {
|
|
'functionName': 'Classifier.smileNaiveBayes',
|
|
'arguments': {'lambda': {'constantValue': 0.01}},
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
# Result of ee.Classifier.load('a')
|
|
_CLASSIFIER_LOAD_A = {
|
|
'functionInvocationValue': {
|
|
'functionName': 'Classifier.load',
|
|
'arguments': {'id': {'constantValue': 'a'}},
|
|
}
|
|
}
|
|
|
|
|
|
def make_expression_graph(
|
|
function_invocation_value: dict[str, Any],
|
|
) -> dict[str, Any]:
|
|
return {
|
|
'result': '0',
|
|
'values': {'0': {'functionInvocationValue': function_invocation_value}},
|
|
}
|
|
|
|
|
|
class ClassifierTest(apitestcase.ApiTestCase):
|
|
|
|
def test_naive_bayes_args(self):
|
|
classifier = ee.Classifier.smileNaiveBayes(0.01)
|
|
self.assertEqual({'value': 'fakeValue'}, classifier.getInfo())
|
|
|
|
naive_bayes_func = ee.ApiFunction.lookup('Classifier.smileNaiveBayes')
|
|
self.assertEqual(naive_bayes_func, classifier.func)
|
|
self.assertFalse(classifier.isVariable())
|
|
|
|
result = json.loads(classifier.serialize())
|
|
self.assertEqual(_CLASSIFIER_JSON, result)
|
|
|
|
def test_square_kwargs(self):
|
|
kwargs = {'lambda': 0.01}
|
|
classifier = ee.Classifier.smileNaiveBayes(**kwargs)
|
|
result = json.loads(classifier.serialize())
|
|
self.assertEqual(_CLASSIFIER_JSON, result)
|
|
|
|
def test_cast(self):
|
|
classifier = ee.Classifier(ee.Classifier.smileNaiveBayes(0.01))
|
|
result = json.loads(classifier.serialize())
|
|
self.assertEqual(_CLASSIFIER_JSON, result)
|
|
|
|
@unittest.skipIf(sys.version_info < (3, 10), 'Unsupported in Python <= 3.9')
|
|
def test_no_args(self):
|
|
message = (
|
|
r'Classifier\.__init__\(\) missing 1 required positional argument:'
|
|
r' \'classifier\''
|
|
)
|
|
with self.assertRaisesRegex(TypeError, message):
|
|
ee.Classifier() # pytype:disable=missing-parameter
|
|
|
|
def test_wrong_type(self):
|
|
message = (
|
|
r'Classifier can only be used as a cast to Classifier\. Found <class'
|
|
r' \'int\'>'
|
|
)
|
|
with self.assertRaisesRegex(TypeError, message):
|
|
ee.Classifier(1234) # pytype:disable=wrong-arg-types
|
|
|
|
def test_amnh_maxent(self):
|
|
categorical_names = ['a', 'b']
|
|
output_format = 'cloglog'
|
|
auto_feature = False
|
|
linear = True
|
|
quadratic = False
|
|
product = True
|
|
threshold = False
|
|
hinge = True
|
|
hinge_threshold = 1
|
|
l2lq_threshold = 2
|
|
lq2lqpt_threshold = 3
|
|
add_samples_to_background = False
|
|
add_all_samples_to_background = True
|
|
beta_multiplier = False
|
|
beta_hinge = 4.0
|
|
beta_lqp = 5.0
|
|
beta_categorical = 6.0
|
|
beta_threshold = 7.0
|
|
extrapolate = True
|
|
do_clamp = False
|
|
write_clamp_grid = True
|
|
random_test_points = 8
|
|
seed = 9
|
|
expect = make_expression_graph({
|
|
'arguments': {
|
|
'categoricalNames': {'constantValue': categorical_names},
|
|
'outputFormat': {'constantValue': output_format},
|
|
'autoFeature': {'constantValue': auto_feature},
|
|
'linear': {'constantValue': linear},
|
|
'quadratic': {'constantValue': quadratic},
|
|
'product': {'constantValue': product},
|
|
'threshold': {'constantValue': threshold},
|
|
'hinge': {'constantValue': hinge},
|
|
'hingeThreshold': {'constantValue': hinge_threshold},
|
|
'l2lqThreshold': {'constantValue': l2lq_threshold},
|
|
'lq2lqptThreshold': {'constantValue': lq2lqpt_threshold},
|
|
'addSamplesToBackground': {
|
|
'constantValue': add_samples_to_background
|
|
},
|
|
'addAllSamplesToBackground': {
|
|
'constantValue': add_all_samples_to_background
|
|
},
|
|
'betaMultiplier': {'constantValue': beta_multiplier},
|
|
'betaHinge': {'constantValue': beta_hinge},
|
|
'betaLqp': {'constantValue': beta_lqp},
|
|
'betaCategorical': {'constantValue': beta_categorical},
|
|
'betaThreshold': {'constantValue': beta_threshold},
|
|
'extrapolate': {'constantValue': extrapolate},
|
|
'doClamp': {'constantValue': do_clamp},
|
|
'writeClampGrid': {'constantValue': write_clamp_grid},
|
|
'randomTestPoints': {'constantValue': random_test_points},
|
|
'seed': {'constantValue': seed},
|
|
},
|
|
'functionName': 'Classifier.amnhMaxent',
|
|
})
|
|
expression = ee.Classifier.amnhMaxent(
|
|
categorical_names,
|
|
output_format,
|
|
auto_feature,
|
|
linear,
|
|
quadratic,
|
|
product,
|
|
threshold,
|
|
hinge,
|
|
hinge_threshold,
|
|
l2lq_threshold,
|
|
lq2lqpt_threshold,
|
|
add_samples_to_background,
|
|
add_all_samples_to_background,
|
|
beta_multiplier,
|
|
beta_hinge,
|
|
beta_lqp,
|
|
beta_categorical,
|
|
beta_threshold,
|
|
extrapolate,
|
|
do_clamp,
|
|
write_clamp_grid,
|
|
random_test_points,
|
|
seed,
|
|
)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
expression = ee.Classifier.amnhMaxent(
|
|
categoricalNames=categorical_names,
|
|
outputFormat=output_format,
|
|
autoFeature=auto_feature,
|
|
linear=linear,
|
|
quadratic=quadratic,
|
|
product=product,
|
|
threshold=threshold,
|
|
hinge=hinge,
|
|
hingeThreshold=hinge_threshold,
|
|
l2lqThreshold=l2lq_threshold,
|
|
lq2lqptThreshold=lq2lqpt_threshold,
|
|
addSamplesToBackground=add_samples_to_background,
|
|
addAllSamplesToBackground=add_all_samples_to_background,
|
|
betaMultiplier=beta_multiplier,
|
|
betaHinge=beta_hinge,
|
|
betaLqp=beta_lqp,
|
|
betaCategorical=beta_categorical,
|
|
betaThreshold=beta_threshold,
|
|
extrapolate=extrapolate,
|
|
doClamp=do_clamp,
|
|
writeClampGrid=write_clamp_grid,
|
|
randomTestPoints=random_test_points,
|
|
seed=seed,
|
|
)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_confusion_matrix(self):
|
|
classifier = ee.Classifier.load('a')
|
|
expect = make_expression_graph({
|
|
'arguments': {'classifier': _CLASSIFIER_LOAD_A},
|
|
'functionName': 'Classifier.confusionMatrix',
|
|
})
|
|
expression = classifier.confusionMatrix()
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_decision_tree(self):
|
|
tree_string = 'a'
|
|
expect = make_expression_graph({
|
|
'arguments': {
|
|
'treeString': {'constantValue': tree_string},
|
|
},
|
|
'functionName': 'Classifier.decisionTree',
|
|
})
|
|
expression = ee.Classifier.decisionTree(tree_string)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
expression = ee.Classifier.decisionTree(treeString=tree_string)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_decision_tree_ensemble(self):
|
|
tree_strings = ['a', 'b']
|
|
expect = make_expression_graph({
|
|
'arguments': {
|
|
'treeStrings': {'constantValue': tree_strings},
|
|
},
|
|
'functionName': 'Classifier.decisionTreeEnsemble',
|
|
})
|
|
expression = ee.Classifier.decisionTreeEnsemble(tree_strings)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
expression = ee.Classifier.decisionTreeEnsemble(treeStrings=tree_strings)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_explain(self):
|
|
classifier = ee.Classifier.load('a')
|
|
expect = make_expression_graph({
|
|
'arguments': {'classifier': _CLASSIFIER_LOAD_A},
|
|
'functionName': 'Classifier.explain',
|
|
})
|
|
expression = classifier.explain()
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_libsvm(self):
|
|
decision_procedure = 'Voting'
|
|
svm_type = 'NU_SVC'
|
|
kernel_type = 'POLY'
|
|
shrinking = True
|
|
degree = 1
|
|
gamma = 2.0
|
|
coef0 = 3.0
|
|
cost = 4.0
|
|
nu = 5.0
|
|
termination_epsilon = 6.0
|
|
loss_epsilon = 7.0
|
|
one_class = 0
|
|
expect = make_expression_graph({
|
|
'arguments': {
|
|
'decisionProcedure': {'constantValue': decision_procedure},
|
|
'svmType': {'constantValue': svm_type},
|
|
'kernelType': {'constantValue': kernel_type},
|
|
'shrinking': {'constantValue': shrinking},
|
|
'degree': {'constantValue': degree},
|
|
'gamma': {'constantValue': gamma},
|
|
'coef0': {'constantValue': coef0},
|
|
'cost': {'constantValue': cost},
|
|
'nu': {'constantValue': nu},
|
|
'terminationEpsilon': {'constantValue': termination_epsilon},
|
|
'lossEpsilon': {'constantValue': loss_epsilon},
|
|
'oneClass': {'constantValue': one_class},
|
|
},
|
|
'functionName': 'Classifier.libsvm',
|
|
})
|
|
expression = ee.Classifier.libsvm(
|
|
decision_procedure,
|
|
svm_type,
|
|
kernel_type,
|
|
shrinking,
|
|
degree,
|
|
gamma,
|
|
coef0,
|
|
cost,
|
|
nu,
|
|
termination_epsilon,
|
|
loss_epsilon,
|
|
one_class,
|
|
)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
expression = ee.Classifier.libsvm(
|
|
decisionProcedure=decision_procedure,
|
|
svmType=svm_type,
|
|
kernelType=kernel_type,
|
|
shrinking=shrinking,
|
|
degree=degree,
|
|
gamma=gamma,
|
|
coef0=coef0,
|
|
cost=cost,
|
|
nu=nu,
|
|
terminationEpsilon=termination_epsilon,
|
|
lossEpsilon=loss_epsilon,
|
|
oneClass=one_class,
|
|
)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_load(self):
|
|
id = 'a'
|
|
expect = make_expression_graph({
|
|
'arguments': {
|
|
'id': {'constantValue': id},
|
|
},
|
|
'functionName': 'Classifier.load',
|
|
})
|
|
expression = ee.Classifier.load(id)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
expression = ee.Classifier.load(id=id)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_minimum_distance(self):
|
|
metric = 'cosine'
|
|
k_nearest = 1
|
|
expect = make_expression_graph({
|
|
'arguments': {
|
|
'metric': {'constantValue': metric},
|
|
'kNearest': {'constantValue': k_nearest},
|
|
},
|
|
'functionName': 'Classifier.minimumDistance',
|
|
})
|
|
expression = ee.Classifier.minimumDistance(metric, k_nearest)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
expression = ee.Classifier.minimumDistance(
|
|
metric=metric, kNearest=k_nearest
|
|
)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_mode(self):
|
|
classifier = ee.Classifier.load('a')
|
|
expect = make_expression_graph({
|
|
'arguments': {'classifier': _CLASSIFIER_LOAD_A},
|
|
'functionName': 'Classifier.mode',
|
|
})
|
|
expression = classifier.mode()
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_schema(self):
|
|
classifier = ee.Classifier.load('a')
|
|
expect = make_expression_graph({
|
|
'arguments': {'classifier': _CLASSIFIER_LOAD_A},
|
|
'functionName': 'Classifier.schema',
|
|
})
|
|
expression = classifier.schema()
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_set_output_mode(self):
|
|
classifier = ee.Classifier.load('a')
|
|
mode = 'PROBABILITY'
|
|
expect = make_expression_graph({
|
|
'arguments': {
|
|
'classifier': _CLASSIFIER_LOAD_A,
|
|
'mode': {'constantValue': mode},
|
|
},
|
|
'functionName': 'Classifier.setOutputMode',
|
|
})
|
|
expression = classifier.setOutputMode(mode)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
expression = classifier.setOutputMode(mode=mode)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_smile_cart(self):
|
|
max_nodes = 1
|
|
min_leaf_population = 2
|
|
expect = make_expression_graph({
|
|
'arguments': {
|
|
'maxNodes': {'constantValue': max_nodes},
|
|
'minLeafPopulation': {'constantValue': min_leaf_population},
|
|
},
|
|
'functionName': 'Classifier.smileCart',
|
|
})
|
|
expression = ee.Classifier.smileCart(max_nodes, min_leaf_population)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
expression = ee.Classifier.smileCart(
|
|
maxNodes=max_nodes, minLeafPopulation=min_leaf_population
|
|
)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_smile_gradient_tree_boost(self):
|
|
number_of_trees = 1
|
|
shrinkage = 0.2
|
|
sampling_rate = 0.3
|
|
max_nodes = 4
|
|
loss = 'Huber'
|
|
seed = 5
|
|
expect = make_expression_graph({
|
|
'arguments': {
|
|
'numberOfTrees': {'constantValue': number_of_trees},
|
|
'shrinkage': {'constantValue': shrinkage},
|
|
'samplingRate': {'constantValue': sampling_rate},
|
|
'maxNodes': {'constantValue': max_nodes},
|
|
'loss': {'constantValue': loss},
|
|
'seed': {'constantValue': seed},
|
|
},
|
|
'functionName': 'Classifier.smileGradientTreeBoost',
|
|
})
|
|
expression = ee.Classifier.smileGradientTreeBoost(
|
|
number_of_trees, shrinkage, sampling_rate, max_nodes, loss, seed
|
|
)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
expression = ee.Classifier.smileGradientTreeBoost(
|
|
numberOfTrees=number_of_trees,
|
|
shrinkage=shrinkage,
|
|
samplingRate=sampling_rate,
|
|
maxNodes=max_nodes,
|
|
loss=loss,
|
|
seed=seed,
|
|
)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_smile_knn(self):
|
|
k = 1
|
|
search_method = 'AUTO'
|
|
metric = 'EUCLIDEAN'
|
|
expect = make_expression_graph({
|
|
'arguments': {
|
|
'k': {'constantValue': k},
|
|
'searchMethod': {'constantValue': search_method},
|
|
'metric': {'constantValue': metric},
|
|
},
|
|
'functionName': 'Classifier.smileKNN',
|
|
})
|
|
expression = ee.Classifier.smileKNN(k, search_method, metric)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
expression = ee.Classifier.smileKNN(
|
|
k=k, searchMethod=search_method, metric=metric
|
|
)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_smile_naive_bayes(self):
|
|
lambda_ = 1
|
|
expect = make_expression_graph({
|
|
'arguments': {
|
|
'lambda': {'constantValue': lambda_},
|
|
},
|
|
'functionName': 'Classifier.smileNaiveBayes',
|
|
})
|
|
expression = ee.Classifier.smileNaiveBayes(lambda_)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
# 'lambda' is a reserved word in Python.
|
|
args = {'lambda': lambda_}
|
|
expression = ee.Classifier.smileNaiveBayes(**args)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_smile_naive_bayes_with_bad_kwargs(self):
|
|
message = r"Unexpected arguments: \['bad_arg'\]\. Expected: lambda."
|
|
with self.assertRaisesRegex(ValueError, message):
|
|
ee.Classifier.smileNaiveBayes(bad_arg=1)
|
|
|
|
def test_smile_random_forest(self):
|
|
number_of_trees = 1
|
|
variables_per_split = True
|
|
min_leaf_population = 2
|
|
bag_fraction = 0.5
|
|
max_nodes = False
|
|
seed = 3
|
|
expect = make_expression_graph({
|
|
'arguments': {
|
|
'numberOfTrees': {'constantValue': number_of_trees},
|
|
'variablesPerSplit': {'constantValue': variables_per_split},
|
|
'minLeafPopulation': {'constantValue': min_leaf_population},
|
|
'bagFraction': {'constantValue': bag_fraction},
|
|
'maxNodes': {'constantValue': max_nodes},
|
|
'seed': {'constantValue': seed},
|
|
},
|
|
'functionName': 'Classifier.smileRandomForest',
|
|
})
|
|
expression = ee.Classifier.smileRandomForest(
|
|
number_of_trees,
|
|
variables_per_split,
|
|
min_leaf_population,
|
|
bag_fraction,
|
|
max_nodes,
|
|
seed,
|
|
)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
expression = ee.Classifier.smileRandomForest(
|
|
numberOfTrees=number_of_trees,
|
|
variablesPerSplit=variables_per_split,
|
|
minLeafPopulation=min_leaf_population,
|
|
bagFraction=bag_fraction,
|
|
maxNodes=max_nodes,
|
|
seed=seed,
|
|
)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_spectral_region(self):
|
|
coordinates = [[1, 2], [3, 4]]
|
|
schema = ['a', 'b']
|
|
expect = make_expression_graph({
|
|
'arguments': {
|
|
'coordinates': {'constantValue': coordinates},
|
|
'schema': {'constantValue': schema},
|
|
},
|
|
'functionName': 'Classifier.spectralRegion',
|
|
})
|
|
expression = ee.Classifier.spectralRegion(coordinates, schema)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
expression = ee.Classifier.spectralRegion(
|
|
coordinates=coordinates, schema=schema
|
|
)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
def test_train(self):
|
|
classifier = ee.Classifier.load('a')
|
|
features = ee.FeatureCollection('b')
|
|
class_property = 'c'
|
|
input_properties = ['d', 'e']
|
|
subsampling = 1
|
|
subsampling_seed = 2
|
|
expect = make_expression_graph({
|
|
'arguments': {
|
|
'classifier': _CLASSIFIER_LOAD_A,
|
|
'features': {
|
|
'functionInvocationValue': {
|
|
'functionName': 'Collection.loadTable',
|
|
'arguments': {'tableId': {'constantValue': 'b'}},
|
|
}
|
|
},
|
|
'classProperty': {'constantValue': class_property},
|
|
'inputProperties': {'constantValue': input_properties},
|
|
'subsampling': {'constantValue': subsampling},
|
|
'subsamplingSeed': {'constantValue': subsampling_seed},
|
|
},
|
|
'functionName': 'Classifier.train',
|
|
})
|
|
expression = classifier.train(
|
|
features,
|
|
class_property,
|
|
input_properties,
|
|
subsampling,
|
|
subsampling_seed,
|
|
)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
expression = classifier.train(
|
|
features=features,
|
|
classProperty=class_property,
|
|
inputProperties=input_properties,
|
|
subsampling=subsampling,
|
|
subsamplingSeed=subsampling_seed,
|
|
)
|
|
result = json.loads(expression.serialize())
|
|
self.assertEqual(expect, result)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|