source: tags/MetisMQIDemo/src/main/java/weka/core/json/JSONInstances.java

Last change on this file was 29, checked in by gnappo, 15 years ago

Taggata versione per la demo e aggiunto branch.

File size: 11.6 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 * JSONInstances.java
19 * Copyright (C) 2009 University of Waikato, Hamilton, New Zealand
20 */
21
22package weka.core.json;
23
24import weka.core.Attribute;
25import weka.core.Instance;
26import weka.core.Instances;
27import weka.core.DenseInstance;
28import weka.core.SparseInstance;
29import weka.core.Utils;
30import weka.core.converters.ConverterUtils.DataSource;
31
32import java.util.ArrayList;
33
34/**
35 * Class for transforming Instances objects into <a href="http://www.json.org/" target="_blank">JSON</a>
36 * objects and vice versa.
37 *
38 * @author  FracPete (fracpete at waikato dot ac dot nz)
39 * @version $Revision: 5987 $
40 */
41public class JSONInstances {
42
43  /** the header section. */
44  public final static String HEADER = "header";
45
46  /** the data section. */
47  public final static String DATA = "data";
48
49  /** the relation name. */
50  public final static String RELATION = "relation";
51
52  /** the attributes object. */
53  public final static String ATTRIBUTES = "attributes";
54
55  /** the name attribute. */
56  public final static String NAME = "name";
57
58  /** the type attribute. */
59  public final static String TYPE = "type";
60
61  /** the class attribute indicator. */
62  public final static String CLASS = "class";
63
64  /** the labels attribute. */
65  public final static String LABELS = "labels";
66
67  /** the weight attribute. */
68  public final static String WEIGHT = "weight";
69
70  /** the dateformat attribute. */
71  public final static String DATEFORMAT = "dateformat";
72
73  /** the sparse attribute. */
74  public final static String SPARSE = "sparse";
75
76  /** the values attribute. */
77  public final static String VALUES = "values";
78
79  /** the separator for index/value in case of sparse instances. */
80  public final static String SPARSE_SEPARATOR = ":";
81 
82  /**
83   * Turns the JSON object into an Attribute, if possible.
84   *
85   * @param att         the JSON object to turn into an Attribute
86   * @param classAtt    for storing whether the attribute is the class attribute
87   * @return            the Attribute, null in case of an error
88   */
89  protected static Attribute toAttribute(JSONNode att, boolean[] classAtt) {
90    Attribute   result;
91    String      name;
92    String      type;
93    String      dateformat;
94    JSONNode    labels;
95    ArrayList<String>   values;
96    int         i;
97    double      weight;
98   
99    name   = (String) att.getChild(NAME).getValue("noname");
100    type   = (String) att.getChild(TYPE).getValue("");
101    weight = (Double) att.getChild(WEIGHT).getValue(new Double(1.0));
102    if (type.equals(Attribute.typeToString(Attribute.NUMERIC))) {
103      result = new Attribute(name);
104    }
105    else if (type.equals(Attribute.typeToString(Attribute.NOMINAL))) {
106      labels = att.getChild(LABELS);
107      values = new ArrayList<String>();
108      for (i = 0; i < labels.getChildCount(); i++)
109        values.add((String)((JSONNode) labels.getChildAt(i)).getValue());
110      result = new Attribute(name, values);
111    }
112    else if (type.equals(Attribute.typeToString(Attribute.DATE))) {
113      dateformat = (String) att.getChild(TYPE).getValue("yyyy-MM-dd'T'HH:mm:ss");
114      result     = new Attribute(name, dateformat);
115    }
116    else if (type.equals(Attribute.typeToString(Attribute.STRING))) {
117      result = new Attribute(name, (ArrayList<String>) null);
118    }
119    else {
120      System.err.println("Unhandled attribute type '" + type + "'!");
121      return null;
122    }
123    result.setWeight(weight);
124   
125    return result;
126  }
127
128  /**
129   * Turns the JSON Object into an Instance, if possible.
130   *
131   * @param inst        the JSON object to turn into an Instance
132   * @param data        the data so far (only used for header information)
133   * @return            the Instance, null in case of an error
134   */
135  protected static Instance toInstance(JSONNode inst, Instances data) {
136    Instance    result;
137    boolean     sparse;
138    double      weight;
139    JSONNode    values;
140    int         i;
141    int         index;
142    int         pos;
143    String      value;
144    double[]    vals;
145
146    sparse = (Boolean) inst.getChild(SPARSE).getValue(new Boolean(false));
147    weight = (Double) inst.getChild(WEIGHT).getValue(new Double(1.0));
148    values = inst.getChild(VALUES);
149    vals   = new double[data.numAttributes()];
150    for (i = 0; i < values.getChildCount(); i++) {
151      if (sparse) {
152        value = "" + ((JSONNode) values.getChildAt(i)).getValue();
153        pos   = value.indexOf(SPARSE_SEPARATOR);
154        index = Integer.parseInt(value.substring(0, pos));
155        value = value.substring(pos + 1);
156      }
157      else {
158        index = i;
159        value = "" + ((JSONNode) values.getChildAt(i)).getValue();
160      }
161     
162      try {
163        if (data.attribute(index).isNumeric()) {
164          vals[index] = Double.parseDouble(value);
165        }
166        else if (data.attribute(index).isNominal()) {
167          vals[index] = data.attribute(index).indexOfValue(value);
168          if ((vals[index] == -1) && value.startsWith("'") && value.endsWith("'"))
169            vals[index] = data.attribute(index).indexOfValue(Utils.unquote(value));
170          if (vals[index] == -1) {
171            System.err.println("Unknown label '" + value + "' for attribute #" + (index+1) + "!");
172            return null;
173          }
174        }
175        else if (data.attribute(index).isDate()) {
176          vals[index] = data.attribute(index).parseDate(value);
177        }
178        else if (data.attribute(index).isString()) {
179          vals[index] = data.attribute(index).addStringValue(value);
180        }
181        else {
182          System.err.println("Unhandled attribute type '" + Attribute.typeToString(data.attribute(index).type()) + "'!");
183          return null;
184        }
185      }
186      catch (Exception e) {
187        System.err.println("Error parsing value #" + (index+1) + ": " + e.toString());
188        return null;
189      }
190    }
191
192    result = new DenseInstance(weight, vals);
193    result.setDataset(data);
194     
195    return result;
196  }
197 
198  /**
199   * Turns a JSON object, if possible, into an Instances object.
200   *
201   * @param json        the JSON object to convert
202   * @param onlyHeader  whether to retrieve only the header
203   * @return            the generated Instances object, null if not possible
204   */
205  protected static Instances toInstances(JSONNode json, boolean onlyHeader) {
206    Instances   result;
207    JSONNode    header;
208    JSONNode    attributes;
209    JSONNode    data;
210    ArrayList<Attribute>        atts;
211    Attribute   att;
212    Instance    inst;
213    int         i;
214    int         classIndex;
215    boolean[]   classAtt;
216   
217    header = json.getChild(HEADER);
218    if (header == null) {
219      System.err.println("No '" + HEADER + "' section!");
220      return null;
221    }
222    data = json.getChild(DATA);
223    if (data == null) {
224      System.err.println("No '" + DATA + "' section!");
225      return null;
226    }
227   
228    // attributes
229    attributes = header.getChild(ATTRIBUTES);
230    if (attributes == null) {
231      System.err.println("No '" + ATTRIBUTES + "' array!");
232      return null;
233    }
234    atts       = new ArrayList<Attribute>();
235    classAtt   = new boolean[1];
236    classIndex = -1;
237    for (i = 0; i < attributes.getChildCount(); i++) {
238      att = toAttribute((JSONNode) attributes.getChildAt(i), classAtt);
239      if (att == null) {
240        System.err.println("Could not convert attribute #" + (i+1) + "!");
241        return null;
242      }
243      if (classAtt[0])
244        classIndex = i;
245      atts.add(att);
246    }
247    result = new Instances(
248        header.getChild(RELATION).getValue("unknown").toString(), 
249        atts, 
250        (onlyHeader ? 0 : data.getChildCount()));
251    result.setClassIndex(classIndex);
252   
253    // data
254    if (!onlyHeader) {
255      for (i = 0; i < data.getChildCount(); i++) {
256        inst = toInstance((JSONNode) data.getChildAt(i), result);
257        if (inst == null) {
258          System.err.println("Could not convert instance #" + (i+1) + "!");
259          return null;
260        }
261        result.add(inst);
262      }
263    }
264   
265    return result;
266  }
267 
268  /**
269   * Turns a JSON object, if possible, into an Instances object.
270   *
271   * @param json        the JSON object to convert
272   * @return            the generated Instances object, null if not possible
273   */
274  public static Instances toInstances(JSONNode json) {
275    return toInstances(json, false);
276  }
277 
278  /**
279   * Turns a JSON object, if possible, into an Instances object (only header).
280   *
281   * @param json        the JSON object to convert
282   * @return            the generated Instances header object, null if not possible
283   */
284  public static Instances toHeader(JSONNode json) {
285    return toInstances(json, true);
286  }
287 
288  /**
289   * Turns the Attribute into a JSON object.
290   *
291   * @param inst        the corresponding dataset
292   * @param att         the attribute to convert
293   * @return            the JSON object
294   */
295  protected static JSONNode toJSON(Instances inst, Attribute att) {
296    JSONNode    result;
297    JSONNode    labels;
298    int         i;
299   
300    result = new JSONNode();
301
302    result.addPrimitive(NAME, att.name());
303    result.addPrimitive(TYPE, Attribute.typeToString(att));
304    result.addPrimitive(CLASS, (att.index() == inst.classIndex()));
305    result.addPrimitive(WEIGHT, att.weight());
306    if (att.isNominal()) {
307      labels = result.addArray(LABELS);
308      for (i = 0; i < att.numValues(); i++)
309        labels.addArrayElement(att.value(i));
310    }
311    if (att.isDate())
312      result.addPrimitive(DATEFORMAT, att.getDateFormat());
313   
314    return result;
315  }
316 
317  /**
318   * Turns the Instance into a JSON object.
319   *
320   * @param inst        the Instance to convert
321   * @return            the JSON object
322   */
323  protected static JSONNode toJSON(Instance inst) {
324    JSONNode    result;
325    JSONNode    values;
326    int         i;
327    boolean     sparse;
328   
329    result = new JSONNode();
330   
331    sparse = (inst instanceof SparseInstance);
332    result.addPrimitive(SPARSE, sparse);
333    result.addPrimitive(WEIGHT, inst.weight());
334    values = result.addArray(VALUES);
335    if (sparse) {
336      for (i = 0; i < inst.numValues(); i++)
337        values.addArrayElement(inst.index(i) + SPARSE_SEPARATOR + inst.toString(inst.index(i)));
338    }
339    else {
340      for (i = 0; i < inst.numAttributes(); i++)
341        values.addArrayElement(inst.toString(i));
342    }
343   
344    return result;
345  }
346 
347  /**
348   * Turns the Instances object into a JSON object.
349   *
350   * @param inst        the Instances to turn into a JSON object
351   * @return            the JSON object
352   */
353  public static JSONNode toJSON(Instances inst) {
354    JSONNode    result;
355    JSONNode    header;
356    JSONNode    atts;
357    JSONNode    data;
358    int         i;
359   
360    result = new JSONNode();
361   
362    // header
363    header = result.addObject(HEADER);
364    header.addPrimitive(RELATION, inst.relationName());
365    atts = header.addArray(ATTRIBUTES);
366    for (i = 0; i < inst.numAttributes(); i++)
367      atts.add(toJSON(inst, inst.attribute(i)));
368   
369    // data
370    data = result.addArray(DATA);
371    for (i = 0; i < inst.numInstances(); i++)
372      data.add(toJSON(inst.instance(i)));
373   
374    return result;
375  }
376 
377  /**
378   * For testing only.
379   *
380   * @param args        expects a dataset as first parameter
381   * @throws Exception  if something goes wrong
382   */
383  public static void main(String[] args) throws Exception {
384    if (args.length != 1) {
385      System.err.println("No dataset supplied!");
386      System.exit(1);
387    }
388
389    // load dataset
390    Instances data = DataSource.read(args[0]);
391   
392    // turn Instances into JSON object and output it
393    JSONNode json = toJSON(data);
394    StringBuffer buffer = new StringBuffer();
395    json.toString(buffer);
396    System.out.println(buffer.toString());
397   
398    // turn JSON object back into Instances and output it
399    Instances inst = toInstances(json);
400    System.out.println(inst);
401  }
402}
Note: See TracBrowser for help on using the repository browser.