I am using a string to vector filter to convert my arff to vector format.
But it throws an exception
weka.core.WekaException: weka.classifiers.bayes.NaiveBayesMultinomialUpdateable: Not enough training instances with class labels (required: 1, provided: 0)!
I tried to use the same on weka explorer and it worked fine.
This is my code
ArffLoader loader = new ArffLoader();
loader.setFile(new File("valid file"));
Instances structure = loader.getStructure();
structure.setClassIndex(0);
// train NaiveBayes
NaiveBayesMultinomialUpdateable n = new NaiveBayesMultinomialUpdateable();
FilteredClassifier f = new FilteredClassifier();
StringToWordVector s = new StringToWordVector();
f.setFilter(s);
f.setClassifier(n);
f.buildClassifier(structure);
Instance current;
while ((current = loader.getNextInstance(structure)) != null)
n.updateClassifier(current);
// output generated model
System.out.println(n);
I have tried another example but it still does not work
ArffLoader loader = new ArffLoader();
loader.setFile(new File("valid file"));
Instances structure = loader.getStructure();
// train NaiveBayes
NaiveBayesMultinomialUpdateable n = new NaiveBayesMultinomialUpdateable();
FilteredClassifier f = new FilteredClassifier();
StringToWordVector s = new StringToWordVector();
s.setInputFormat(structure);
Instances struct = Filter.useFilter(structure, s);
struct.setClassIndex(0);
System.out.println(struct.numAttributes()); // only gives 2 or 1 attributes
n.buildClassifier(struct);
Instance current;
while ((current = loader.getNextInstance(struct)) != null)
n.updateClassifier(current);
// output generated model
System.out.println(n);
The number of attributes printed is always 2 or 1.
It seems the string to word vector isn't working as expected
Original folder : https://www.dropbox.com/sh/cma4hbe2r96ul1c/GL2wNdeVUz
Converted to arff: https://www.dropbox.com/s/efle6ci4lb5riq7/test1.arff
According to your arff, the class seems to be the second in the two attributes, so the problem can be here:
struct.setClassIndex(0);
try
struct.setClassIndex(1);
UPDATE: I made this change to the first example, and it gives no exception, and prints out:
The independent probability of a class
--------------------------------------
oil spill 40.0
police 989.0
The probability of a word given the class
-----------------------------------------
oil spill police
class Infinity Infinity
Related
I am using Weka to do a text classification. I have created a NaiveBayes model using the Weka GUI, and I have saved that model and then was trying to use this model to classify instances of a training set. This is my code :
Classifier clsClassifier = (Classifier) weka.core.SerializationHelper.read("Source/test/80percentModel.model");
StringToWordVector filter = new StringToWordVector();
BufferedReader reader = new BufferedReader(
new FileReader("Source/test/clt.train.arff"));
Instances trainingData = new Instances(reader);
reader.close();
trainingData.setClassIndex(trainingData.numAttributes() - 1);
filter.setInputFormat(trainingData);
BufferedReader reader2 = new BufferedReader(
new FileReader("Source/test/clt.test.arff"));
Instances testingData = new Instances(reader2);
reader2.close();
testingData.setClassIndex(testingData.numAttributes() - 1);
testingData = Filter.useFilter(testingData, filter);
System.out.println(testingData.numInstances());
for (int j = 0; j < testingData.numInstances(); j++) {
double res = clsClassifier.classifyInstance(testingData.get(j));
System.out.println(testingData.classAttribute().value((int)res));
}
I am getting the following error :
java.lang.IllegalArgumentException: Src and Dest differ in # of attributes: 1 != 1781
at weka.core.RelationalLocator.copyRelationalValues(RelationalLocator.java:87)
at weka.filters.Filter.copyValues(Filter.java:405)
at weka.filters.Filter.push(Filter.java:326)
at weka.filters.unsupervised.attribute.StringToWordVector.input(StringToWordVector.java:655)
at weka.classifiers.meta.FilteredClassifier.filterInstance(FilteredClassifier.java:672)
at weka.classifiers.meta.FilteredClassifier.distributionForInstance(FilteredClassifier.java:699)
at weka.classifiers.AbstractClassifier.classifyInstance(AbstractClassifier.java:173)
at test.WekaClassification.main(WekaClassification.java:66)
I dont quite get what I am doing wrong here. Why is there a mismatch in the number of attributes ? and is this the correct way to apply a trained model in a testData set ?
There are some possibilities, but your Error Shows that the number of attributes are not equal in your trained and dataset. They must be completely in a same format, type and value. The test file also should have the values for the label attribute. Check to see if in Weka GUI you get the same Error or not? StringToWordVector may not Filter in an affordable manner. check its output by watching the contents.
I have a project where I want to load in a given shapefile, and pick out polygons above a certain size before writing the results to a new shapefile. Maybe not the most efficient, but I've got code that successfully does all of that, right up to the point where it is supposed to write the shapefile. I get no errors, but the resulting shapefile has no usable data in it. I've followed as many tutorials as possible, but still I'm coming up blank.
The first bit of code is where I read in a shapefile, pickout the polygons I want, and put then into a feature collection. This part seems to work fine as far as I can tell.
public class ShapefileTest {
public static void main(String[] args) throws MalformedURLException, IOException, FactoryException, MismatchedDimensionException, TransformException, SchemaException {
File oldShp = new File("Old.shp");
File newShp = new File("New.shp");
//Get data from the original ShapeFile
Map<String, Object> map = new HashMap<String, Object>();
map.put("url", oldShp.toURI().toURL());
//Connect to the dataStore
DataStore dataStore = DataStoreFinder.getDataStore(map);
//Get the typeName from the dataStore
String typeName = dataStore.getTypeNames()[0];
//Get the FeatureSource from the dataStore
FeatureSource<SimpleFeatureType, SimpleFeature> source = dataStore.getFeatureSource(typeName);
SimpleFeatureCollection collection = (SimpleFeatureCollection) source.getFeatures(); //Get all of the features - no filter
//Start creating the new Shapefile
final SimpleFeatureType TYPE = createFeatureType(); //Calls a method that builds the feature type - tested and works.
DefaultFeatureCollection newCollection = new DefaultFeatureCollection(); //To hold my new collection
try (FeatureIterator<SimpleFeature> features = collection.features()) {
while (features.hasNext()) {
SimpleFeature feature = features.next(); //Get next feature
SimpleFeatureBuilder fb = new SimpleFeatureBuilder(TYPE); //Create a new SimpleFeature based on the original
Integer level = (Integer) feature.getAttribute(1); //Get the level for this feature
MultiPolygon multiPoly = (MultiPolygon) feature.getDefaultGeometry(); //Get the geometry collection
//First count how many new polygons we will have
int numNewPoly = 0;
for (int i = 0; i < multiPoly.getNumGeometries(); i++) {
double area = getArea(multiPoly.getGeometryN(i));
if (area > 20200) {
numNewPoly++;
}
}
//Now build an array of the larger polygons
Polygon[] polys = new Polygon[numNewPoly]; //Array of new geometies
int iPoly = 0;
for (int i = 0; i < multiPoly.getNumGeometries(); i++) {
double area = getArea(multiPoly.getGeometryN(i));
if (area > 20200) { //Write the new data
polys[iPoly] = (Polygon) multiPoly.getGeometryN(i);
iPoly++;
}
}
GeometryFactory gf = new GeometryFactory(); //Create a geometry factory
MultiPolygon mp = new MultiPolygon(polys, gf); //Create the MultiPolygonyy
fb.add(mp); //Add the geometry collection to the feature builder
fb.add(level);
fb.add("dBA");
SimpleFeature newFeature = SimpleFeatureBuilder.build( TYPE, new Object[]{mp, level,"dBA"}, null );
newCollection.add(newFeature); //Add it to the collection
}
At this point I have a collection that looks right - it has the correct bounds and everything. The next bit if code is where I put it into a new Shapefile.
//Time to put together the new Shapefile
Map<String, Serializable> newMap = new HashMap<String, Serializable>();
newMap.put("url", newShp.toURI().toURL());
newMap.put("create spatial index", Boolean.TRUE);
DataStore newDataStore = DataStoreFinder.getDataStore(newMap);
newDataStore.createSchema(TYPE);
String newTypeName = newDataStore.getTypeNames()[0];
SimpleFeatureStore fs = (SimpleFeatureStore) newDataStore.getFeatureSource(newTypeName);
Transaction t = new DefaultTransaction("add");
fs.setTransaction(t);
fs.addFeatures(newCollection);
t.commit();
ReferencedEnvelope env = fs.getBounds();
}
}
I put in the very last code to check the bounds of the FeatureStore fs, and it comes back null. Obviously, loading the newly created shapefile (which DOES get created and is ab out the right size), nothing shows up.
The solution actually had nothing to do with the code I posted - it had everything to do with my FeatureType definition. I did not include the "the_geom" to my polygon feature type, so nothing was getting written to the file.
I believe you are missing the step to finalize/close the file. Try adding this after the the t.commit line.
fs.close();
As an expedient alternative, you might try out the Shapefile dumper utility mentioned in the Shapefile DataStores docs. Using that may simplify your second code block into two or three lines.
I train and create a J48 model use WEKA Java Api.
Then, I use classifyInstance() to classify my instance.
but the result is wrong.
my code id following:
Instances train = reader.getDataSet();
Instances test = reader_test.getDataSet();
train.setClassIndex(train.numAttributes() - 1);
Classifier cls = new J48();
cls.buildClassifier(train);
test.setClassIndex(test.numAttributes() - 1);
for(int i = 0; i < test.numInstances(); i++){
Instance inst = test.instance(i);
double result = cls.classifyInstance(inst);
System.out.println(train.classAttribute().value((int)r));
}
The result always equal 0.0
Finally, I use test.insertAttributeAt() before test.setClassIndex().
as following:
test.insertAttributeAt(train.attribute(train.numAttributes() - 1), test.numAttributes());
The result become right. I am very surprising!
however, most documents are not use the function to inserAttribute.
I want to understand why the result become right suddenly.
It will help you.
BufferedReader datafile = readDataFile(TrainingFile);
Instances train = new Instances(datafile);
data.setClassIndex(data.numAttributes() - 1);
Classifier cls = new J48();
cls.buildClassifier(train);
DataSource testDataset = new DataSource(Test);
Instances test = testDataset.getDataSet();
Testdata.setClassIndex(Testdata.numAttributes() - 1);
for(int i = 0; i < test.numInstances(); i++){
Instance inst = test.instance(i);
double actualClassValue = test.instance(i).classValue();
//it will print your class value
String actual=test.classAttribute().value((int)actualClassValue);
double result = cls.classifyInstance(inst);
//will print your predicted value
String prediction=test.classAttribute().value((int)result );
}
you don't need to use insertAttributeAt now.
File Conversion Code
// load CSV
CSVLoader loader = new CSVLoader();
String InputFilename = "TrainingFileName";
loader.setSource(new File(InputFilename));
Instances data = loader.getDataSet();
// save ARFF
ArffSaver saver = new ArffSaver();
saver.setInstances(data);
String FileT = Filename+".arff";
saver.setFile(new File(Path+Directory+"\\"+FileT));
saver.writeBatch();
Change accordingly.
Thanks
I wrote a WEKA java code to train 4 classifiers. I saved the classifiers models and want to use them to predict new unseen instances (think about it as someone who wants to test whether a tweet is positive or negative).
I used StringToWordsVector filter on the training data. And to avoid the "Src and Dest differ in # of attributes" error I used the following code to train the filter using the trained data before applying the filter on the new instance to try and predict whether a new instance is positive or negative. And I just can't get it right.
Classifier cls = (Classifier) weka.core.SerializationHelper.read("models/myModel.model"); //reading one of the trained classifiers
BufferedReader datafile = readDataFile("Tweets/tone1.ARFF"); //read training data
Instances data = new Instances(datafile);
data.setClassIndex(data.numAttributes() - 1);
Filter filter = new StringToWordVector(50);//keep 50 words
filter.setInputFormat(data);
Instances filteredData = Filter.useFilter(data, filter);
// rebuild classifier
cls.buildClassifier(filteredData);
String testInstance= "Text that I want to use as an unseen instance and predict whether it's positive or negative";
System.out.println(">create test instance");
FastVector attributes = new FastVector(2);
attributes.addElement(new Attribute("text", (FastVector) null));
// Add class attribute.
FastVector classValues = new FastVector(2);
classValues.addElement("Negative");
classValues.addElement("Positive");
attributes.addElement(new Attribute("Tone", classValues));
// Create dataset with initial capacity of 100, and set index of class.
Instances tests = new Instances("test istance", attributes, 100);
tests.setClassIndex(tests.numAttributes() - 1);
Instance test = new Instance(2);
// Set value for message attribute
Attribute messageAtt = tests.attribute("text");
test.setValue(messageAtt, messageAtt.addStringValue(testInstance));
test.setDataset(tests);
Filter filter2 = new StringToWordVector(50);
filter2.setInputFormat(tests);
Instances filteredTests = Filter.useFilter(tests, filter2);
System.out.println(">train Test filter using training data");
Standardize sfilter = new Standardize(); //Match the number of attributes between src and dest.
sfilter.setInputFormat(filteredData); // initializing the filter with training set
filteredTests = Filter.useFilter(filteredData, sfilter); // create new test set
ArffSaver saver = new ArffSaver(); //save test data to ARFF file
saver.setInstances(filteredTests);
File unseenFile = new File ("Tweets/unseen.ARFF");
saver.setFile(unseenFile);
saver.writeBatch();
When I try to Standardize the Input data using the filtered training data I get a new ARFF file (unseen.ARFF) but with 2000 (same number of training data) instances where most of the values are negative. I don't understand why or how to remove those instances.
System.out.println(">Evaluation"); //without the following 2 lines I get ArrayIndexOutOfBoundException.
filteredData.setClassIndex(filteredData.numAttributes() - 1);
filteredTests.setClassIndex(filteredTests.numAttributes() - 1);
Evaluation eval = new Evaluation(filteredData);
eval.evaluateModel(cls, filteredTests);
System.out.println(eval.toSummaryString("\nResults\n======\n", false));
Printing the evaluation results I want to see for example a percentage of how positive or negative this instance is but instead I get the following. I also want to see 1 instance instead of 2000. Any help on how to do this will be great.
> Results
======
Correlation coefficient 0.0285
Mean absolute error 0.8765
Root mean squared error 1.2185
Relative absolute error 409.4123 %
Root relative squared error 121.8754 %
Total Number of Instances 2000
Thanks
use eval.predictions(). It is an java.util.ArrayList<Prediction>. Then you can use Prediction.weight() method to get how much positive or negative your test variable is....
cls.distributionForInstance(newInst) returns the probability distribution for an instance. Check the docs
I have reached a good solution and here I share my code with you. This trains a classifier using WEKA Java code then use it to predict new unseen instances. Some parts - like paths - are hardcoded but you can easily modify the method to take parameters.
/**
* This method performs classification of unseen instance.
* It starts by training a model using a selection of classifiers then classifiy new unlabled instances.
*/
public static void predict() throws Exception {
//start by providing the paths for your training and testing ARFF files make sure both files have the same structure and the exact classes in the header
//initialise classifier
Classifier classifier = null;
System.out.println("read training arff");
Instances train = new Instances(new BufferedReader(new FileReader("Train.arff")));
train.setClassIndex(0);//in my case the class was the first attribute thus zero otherwise it's the number of attributes -1
System.out.println("read testing arff");
Instances unlabeled = new Instances(new BufferedReader(new FileReader("Test.arff")));
unlabeled.setClassIndex(0);
// training using a collection of classifiers (NaiveBayes, SMO (AKA SVM), KNN and Decision trees.)
String[] algorithms = {"nb","smo","knn","j48"};
for(int w=0; w<algorithms.length;w++){
if(algorithms[w].equals("nb"))
classifier = new NaiveBayes();
if(algorithms[w].equals("smo"))
classifier = new SMO();
if(algorithms[w].equals("knn"))
classifier = new IBk();
if(algorithms[w].equals("j48"))
classifier = new J48();
System.out.println("==========================================================================");
System.out.println("training using " + algorithms[w] + " classifier");
Evaluation eval = new Evaluation(train);
//perform 10 fold cross validation
eval.crossValidateModel(classifier, train, 10, new Random(1));
String output = eval.toSummaryString();
System.out.println(output);
String classDetails = eval.toClassDetailsString();
System.out.println(classDetails);
classifier.buildClassifier(train);
}
Instances labeled = new Instances(unlabeled);
// label instances (use the trained classifier to classify new unseen instances)
for (int i = 0; i < unlabeled.numInstances(); i++) {
double clsLabel = classifier.classifyInstance(unlabeled.instance(i));
labeled.instance(i).setClassValue(clsLabel);
System.out.println(clsLabel + " -> " + unlabeled.classAttribute().value((int) clsLabel));
}
//save the model for future use
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("myModel.dat"));
out.writeObject(classifier);
out.close();
System.out.println("===== Saved model =====");
}
I need to iteratively extend a weka ARFF file with SparseInstance objects. Each time a new SparseInstance is added the header might change since the new Instance might add additional attributes. I thought the mergeInstances method would solve my problem but it does not. It requires both dataset to have no shared attributes.
If this is not absolutely clear look at the following example:
Dataset1
a b c
1 2 3
4 5 6
Dataset2
c d
7 8
Merged result:
a b c d
1 2 3 ?
4 5 6 ?
? ? 7 8
The only solution I see at the moment is parsing the arff file by hand and merging it using String processing. Does anyone know of a better solution?
Ok. I found the solution myself. The central part of the solution is the method Instances#insertAttributeAt, which inserts a new attribute as the last one if the second parameter is model.numAttributes(). Here is some example code for numerical attributes. It is easy to adapt to other types of attributes as well:
Map<String,String> currentInstanceFeatures = currentInstance.getFeatures();
Instances model = null;
try {
if (targetFile.exists()) {
FileReader in = new FileReader(targetFile);
try {
BufferedReader reader = new BufferedReader(in);
ArffReader arff = new ArffReader(reader);
model = arff.getData();
} finally {
IOUtils.closeQuietly(in);
}
} else {
FastVector schema = new FastVector();
model = new Instances("model", schema, 1);
}
Instance newInstance = new SparseInstance(0);
newInstance.setDataset(model);
for(Map.Entry<String,String> feature:currentInstanceFeatures.entrySet()) {
Attribute attribute = model.attribute(feature.getKey());
if (attribute == null) {
attribute = new Attribute(feature.getKey());
model.insertAttributeAt(attribute, model.numAttributes());
attribute = model.attribute(feature.getKey());
}
newInstance.setValue(attribute, feature.getValue());
}
model.add(newInstance);
model.compactify();
ArffSaver saver = new ArffSaver();
saver.setInstances(model);
saver.setFile(targetFile);
LOGGER.debug("Saving dataset to: " + targetFile.getAbsoluteFile());
saver.writeBatch();
} catch (IOException e) {
throw new IllegalArgumentException(e);
}