/**
* Copyright 2013-2015 Pierre Merienne
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package stormy.pythian.model.instance;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.base.Preconditions;
import storm.trident.tuple.TridentTuple;
public class Instance implements Serializable {
private static final long serialVersionUID = 4970738933759230736L;
public final static String INSTANCE_FIELD = "INSTANCE_FIELD";
public final static String NEW_INSTANCE_FIELD = "NEW_INSTANCE_FIELD";
private transient ListedFeaturesMapper inputListedFeaturesMapper;
private transient NamedFeaturesMapper inputNamedFeaturesMapper;
private transient ListedFeaturesMapper outputListedFeaturesMapper;
private transient NamedFeaturesMapper outputNamedFeaturesMapper;
Feature<?> label;
Map<String, Feature<?>> features;
public Instance() {
this.label = null;
this.features = new HashMap<>();
}
public int size() {
return this.features.size();
}
public boolean hasLabel() {
return this.label != null;
}
public Feature<?> getLabel() {
return this.label;
}
public void setLabel(Feature<?> label) {
this.label = label;
}
public Feature<?> getFeature(String featureName) {
checkNotNull(featureName, "Feature name is mandatory");
checkNotNull(inputNamedFeaturesMapper, "Cannot get feature : no input named features mapper");
String realFeatureName = inputNamedFeaturesMapper.getFeatureName(featureName);
Feature<?> feature = features.get(realFeatureName);
return feature != null ? feature : new NullFeature();
}
public List<Feature<?>> getFeatures() {
checkNotNull(inputListedFeaturesMapper, "Cannot get features : no input listed features mapper");
List<Feature<?>> features = new ArrayList<>(inputListedFeaturesMapper.size());
List<String> selectedFeatureNames = inputListedFeaturesMapper.getSelectedFeatures();
for (String selectedFeatureName : selectedFeatureNames) {
Feature<?> feature = this.features.get(selectedFeatureName);
features.add(feature != null ? feature : new NullFeature());
}
return features;
}
public void setFeature(String featureName, Feature<?> feature) {
checkNotNull(outputNamedFeaturesMapper, "Cannot set feature : no output named features mapper");
String realFeatureName = outputNamedFeaturesMapper.getFeatureName(featureName);
features.put(realFeatureName, feature);
}
public void setFeatures(Map<String, Feature<?>> features) {
for (String featureName : features.keySet()) {
setFeature(featureName, features.get(featureName));
}
}
public void addFeatures(List<Feature<?>> newFeatures) {
checkNotNull(outputListedFeaturesMapper, "Cannot set features : no output listed features mapper");
Preconditions.checkArgument(
outputListedFeaturesMapper.size() == newFeatures.size(),
"Cannot add features, expecting %s new features, got %s", outputListedFeaturesMapper.size(), newFeatures.size());
List<String> selection = outputListedFeaturesMapper.getSelectedFeatures();
for (int i = 0; i < selection.size(); i++) {
this.features.put(selection.get(i), newFeatures.get(i));
}
}
public <T> void process(FeatureProcessor processor) {
checkNotNull(inputListedFeaturesMapper, "Cannot process features : no intput listed features mapper");
List<String> selectedFeatureNames = inputListedFeaturesMapper.getSelectedFeatures();
for (String featureName : selectedFeatureNames) {
Feature<?> feature = this.features.get(featureName);
this.features.put(featureName, processor.process(feature != null ? feature : new NullFeature()));
}
}
public static Instance create(ListedFeaturesMapper outputMapper) {
Instance instance = new Instance();
instance.to(outputMapper);
return instance;
}
public static Instance create(NamedFeaturesMapper outputMapper) {
Instance instance = new Instance();
instance.to(outputMapper);
return instance;
}
public static Instance get(TridentTuple tuple) {
try {
Instance instance = (Instance) tuple.getValueByField(INSTANCE_FIELD);
return instance;
} catch (Exception ex) {
throw new IllegalStateException("No instance found in tuple " + tuple, ex);
}
}
public static Instance get(TridentTuple tuple, ListedFeaturesMapper inputMapper) {
Instance instance = Instance.get(tuple);
return instance.from(inputMapper);
}
public static Instance get(TridentTuple tuple, NamedFeaturesMapper inputMapper) {
Instance instance = Instance.get(tuple);
return instance.from(inputMapper);
}
public static Instance get(TridentTuple tuple, ListedFeaturesMapper inputMapper, ListedFeaturesMapper outputMapper) {
Instance instance = Instance.get(tuple);
return instance.from(inputMapper).to(outputMapper);
}
public static Instance get(TridentTuple tuple, ListedFeaturesMapper inputMapper, NamedFeaturesMapper outputMapper) {
Instance instance = Instance.get(tuple);
return instance.from(inputMapper).to(outputMapper);
}
public static Instance get(TridentTuple tuple, NamedFeaturesMapper inputMapper, ListedFeaturesMapper outputMapper) {
Instance instance = Instance.get(tuple);
return instance.from(inputMapper).to(outputMapper);
}
public static Instance get(TridentTuple tuple, NamedFeaturesMapper inputMapper, NamedFeaturesMapper outputMapper) {
Instance instance = Instance.get(tuple);
return instance.from(inputMapper).to(outputMapper);
}
private Instance from(ListedFeaturesMapper inputListedFeaturesMapper) {
this.inputListedFeaturesMapper = inputListedFeaturesMapper;
this.inputNamedFeaturesMapper = null;
return this;
}
private Instance from(NamedFeaturesMapper inputNamedFeaturesMapper) {
this.inputListedFeaturesMapper = null;
this.inputNamedFeaturesMapper = inputNamedFeaturesMapper;
return this;
}
private Instance to(ListedFeaturesMapper outputListedFeaturesMapper) {
this.outputListedFeaturesMapper = outputListedFeaturesMapper;
this.outputNamedFeaturesMapper = null;
return this;
}
private Instance to(NamedFeaturesMapper outputNamedFeaturesMapper) {
this.outputListedFeaturesMapper = null;
this.outputNamedFeaturesMapper = outputNamedFeaturesMapper;
return this;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((features == null) ? 0 : features.hashCode());
result = prime * result + ((label == null) ? 0 : label.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Instance other = (Instance) obj;
if (features == null) {
if (other.features != null)
return false;
} else if (!features.equals(other.features))
return false;
if (label == null) {
if (other.label != null)
return false;
} else if (!label.equals(other.label))
return false;
return true;
}
@Override
public String toString() {
return "Instance [label=" + label + ", features=" + features + "]";
}
public static interface FeatureProcessor {
Feature<?> process(Feature<?> feature);
}
}