AnnotationMerging.java
001 /**
002  *
003  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
004  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
005  *
006  *  This file is part of GATE (see http://gate.ac.uk/), and is free
007  *  software, licenced under the GNU Library General Public License,
008  *  Version 2, June 1991 (in the distribution as file licence.html,
009  *  and also available at http://gate.ac.uk/gate/licence.html).
010  *
011  *  $Id: IaaCalculation.java 9050 2007-09-04 10:42:12Z yaoyongli $
012  */
013 
014 package gate.util;
015 
016 import gate.Annotation;
017 import gate.AnnotationSet;
018 
019 import java.util.HashMap;
020 import java.util.HashSet;
021 import java.util.Set;
022 import java.util.Vector;
023 /**
024  * Merging the annotations from different annotators. The input
025  * is the array containing the annotation set for merging. The
026  * output is a map, the key of which is the merged annotations
027  * and the values of which represent those annotators who agree
028  * on the merged annotation. Two merging methods are implemented.
029  * One method selects one annotation if at least a pre-defined
030  * number of annotators agree on it. If there are more than 
031  * one merged annotations with the same span, the program selects only
032  * one annotation from them with the maximal number of annotators 
033  * on it. Another method selects only one 
034  * annotation from those annotations with the same span, 
035  * which majority of the annotators support. 
036  */
037 public class AnnotationMerging {
038 
039   /**
040    * Merge all annotationset from an array. If one annotation is in at least
041    * numK annotation sets, then put it into the merging annotation set.
042    */
043   public static void mergeAnnotation(AnnotationSet[] annsArr, String nameFeat,
044     HashMap<Annotation, String> mergeAnns, int numMinK, boolean isTheSameInstances) {
045     int numA = annsArr.length;
046     // First copy the annotatioin sets into a temp array
047     @SuppressWarnings({"unchecked","rawtypes"})
048     Set<Annotation>[] annsArrTemp = new Set[numA];
049     for(int i = 0; i < numA; ++i) {
050       if(annsArr[i!= null) {
051         annsArrTemp[inew HashSet<Annotation>();
052         for(Annotation ann : annsArr[i])
053           annsArrTemp[i].add(ann);
054       }
055     }
056     HashSet<String> featSet = new HashSet<String>();
057     if(nameFeat != nullfeatSet.add(nameFeat);
058     if(numMinK<1numMinK=1;
059     for(int iA = 0; iA < numA - numMinK + 1; ++iA) {
060       if(annsArrTemp[iA!= null) {
061         for(Annotation ann : annsArrTemp[iA]) {
062           int numContained = 1;
063           StringBuffer featAdd = new StringBuffer();
064           featAdd.append(iA);
065           StringBuffer featDisa = new StringBuffer();
066           if(iA>0) {
067             featDisa.append("0");
068             for(int i=1; i<iA; ++i)
069               featDisa.append("-"+i);
070           }
071           int numDisagreed = iA;
072           for(int i = iA + 1; i < numA; ++i) {
073             boolean isContained = false;
074             if(annsArrTemp[i!= null) {
075               Annotation annT = null;
076               for(Annotation ann0 : annsArrTemp[i]) {
077                 if(ann0.isCompatible(ann, featSet)) {
078                   ++numContained;
079                   featAdd.append("-" + i);
080                   annT = ann0;
081                   isContained = true;
082                   break;
083                 }
084               }
085               if(isContained)
086                 annsArrTemp[i].remove(annT);
087             }
088             if(!isContained){
089               if(numDisagreed==0)
090                 featDisa.append(i);
091               else featDisa.append("-"+i);
092               ++numDisagreed;
093             }
094           }
095           if(numContained >= numMinK) {
096             mergeAnns.put(ann, featAdd.toString());
097           else if(isTheSameInstances && nameFeat != null) {
098            ann.getFeatures().remove(nameFeat);
099            mergeAnns.put(ann, featAdd.toString());
100         }
101         }
102       }
103     }
104     //Remove the annotation in the same place
105     removeDuplicate(mergeAnns);
106     return;
107   }
108   /**
109    * Merge all annotationset from an array. If one annotation is agreed by
110    * the majority of the annotators, then put it into the merging annotation set.
111    */
112   public static void mergeAnnotationMajority(AnnotationSet[] annsArr, String nameFeat,
113     HashMap<Annotation, String> mergeAnns, boolean isTheSameInstances) {
114     int numA = annsArr.length;
115     if(nameFeat == null) {
116       mergeAnnogationMajorityNoFeat(annsArr, mergeAnns, isTheSameInstances);
117       return;
118     }
119       
120     // First copy the annotatioin sets into a temp array
121     @SuppressWarnings({"unchecked","rawtypes"})
122     Set<Annotation>[] annsArrTemp = new Set[numA];
123     for(int i = 0; i < numA; ++i) {
124       if(annsArr[i!= null) {
125         annsArrTemp[inew HashSet<Annotation>();
126         for(Annotation ann : annsArr[i])
127           annsArrTemp[i].add(ann);
128       }
129     }
130     for(int iA = 0; iA < numA; ++iA) {
131       if(annsArrTemp[iA!= null) {
132         for(Annotation ann : annsArrTemp[iA]) {
133           int numDisagreed=0;
134           //Already the iA annotators don't agree the annotation
135           numDisagreed = iA;
136           StringBuffer featDisa = new StringBuffer();
137           if(iA>0) {
138             featDisa.append("0");
139             for(int i=1; i<iA; ++i)
140               featDisa.append("-"+i);
141           }
142           HashMap<String,String>featOthers = new HashMap<String,String>();
143           String featTh = null;
144           if(ann.getFeatures().get(nameFeat)!= null)
145               featTh =  ann.getFeatures().get(nameFeat).toString();
146           
147           featOthers.put(featTh, new Integer(iA).toString());
148           HashMap<String,Annotation>annAll = new HashMap<String,Annotation>();
149           annAll.put(featTh, ann);
150           for(int i = iA + 1; i < numA; ++i) {
151             boolean isContained = false;
152             if(annsArrTemp[i!= null) {
153               Annotation annT = null;
154               for(Annotation ann0 : annsArrTemp[i]) {
155                 if(ann0.coextensive(ann)) {
156                   String featValue = null;
157                   if(ann0.getFeatures().get(nameFeat)!=null)
158                     featValue = ann0.getFeatures().get(nameFeat).toString();
159                   if(!featOthers.containsKey(featValue)) {
160                     featOthers.put(featValue, new Integer(i).toString());
161                     annAll.put(featValue, ann0);
162                   }
163                   else {
164                     String str = featOthers.get(featValue);
165                     featOthers.put(featValue, str+"-"+i);
166                   }
167                   annT = ann0;
168                   isContained = true;
169                   break;
170                 }
171               }
172               if(isContained
173                 annsArrTemp[i].remove(annT);
174             }
175             if(!isContained)  {
176               if(numDisagreed==0)
177                 featDisa.append(i);
178               else featDisa.append("-"+i);
179               ++numDisagreed;
180             }
181           }//end of the loop for the following annotation set
182           int numAgreed = -1;
183           String agreeFeat = null;
184           for(String str:featOthers.keySet()) {
185             String str0 = featOthers.get(str);
186             int num=1;
187             while(str0.contains("-")) {
188               ++num;
189               str0 = str0.substring(str0.indexOf('-')+1);
190             }
191             if(numAgreed<num) {
192               numAgreed = num;
193               agreeFeat = str;
194             }
195           }
196           if(numAgreed >= numDisagreed) {
197             mergeAnns.put(annAll.get(agreeFeat), featOthers.get(agreeFeat));
198           else if(isTheSameInstances) {
199             if(ann.getFeatures().get(nameFeat)!= null)
200               ann.getFeatures().remove(nameFeat);
201             mergeAnns.put(ann, featDisa.toString());
202          }
203         //for each ann in the current annotation set
204       }
205     }
206     return;
207   }
208   /** The majority merging method for the annotaiton not specifying any annotation
209    * feature for label. 
210    * */
211   private static void mergeAnnogationMajorityNoFeat(AnnotationSet[] annsArr,
212     HashMap<Annotation, String> mergeAnns, boolean isTheSameInstances) {
213     int numA = annsArr.length;
214     // First copy the annotatioin sets into a temp array
215     @SuppressWarnings({"unchecked","rawtypes"})
216     Set<Annotation>[] annsArrTemp = new Set[numA];
217     for(int i = 0; i < numA; ++i) {
218       if(annsArr[i!= null) {
219         annsArrTemp[inew HashSet<Annotation>();
220         for(Annotation ann : annsArr[i])
221           annsArrTemp[i].add(ann);
222       }
223     }
224     for(int iA = 0; iA < numA; ++iA) {
225       if(annsArrTemp[iA!= null) {
226         for(Annotation ann : annsArrTemp[iA]) {
227           int numDisagreed=0;
228           //Already the iA annotators don't agree the annotation
229           numDisagreed = iA;
230           StringBuffer featDisa = new StringBuffer();
231           if(iA>0) {
232             featDisa.append("0");
233             for(int i=1; i<iA; ++i)
234               featDisa.append("-"+i);
235           }
236           int numAgreed=1;
237           StringBuffer featAdd = new StringBuffer();
238           featAdd.append(iA);
239           for(int i = iA + 1; i < numA; ++i) {
240             boolean isContained = false;
241             if(annsArrTemp[i!= null) {
242               Annotation annT = null;
243               for(Annotation ann0 : annsArrTemp[i]) {
244                 if(ann0.coextensive(ann)) {
245                   ++numAgreed;
246                   annT = ann0;
247                   isContained = true;
248                   featAdd.append("-"+i);
249                   break;
250                 }
251               }
252               if(isContained
253                 annsArrTemp[i].remove(annT);
254             }
255             if(!isContained)  {
256               if(numDisagreed==0)
257                 featDisa.append(i);
258               else featDisa.append("-"+i);
259               ++numDisagreed;
260             }
261           }//end of the loop for the following annotation set
262           if(numAgreed >= numDisagreed) {
263             mergeAnns.put(ann, featAdd.toString());
264           else if(isTheSameInstances) {
265               mergeAnns.put(ann, featAdd.toString());
266          }
267         //for each ann in the current annotation set
268       }
269     }
270     return;
271   }
272   /** Remove the duplicate annotations from the merged annotations. */
273   private static void removeDuplicate(HashMap<Annotation, String> mergeAnns) {
274 //  first copy the annotations into a tempory
275     HashMap <Annotation, Integer> mergeAnnsNum = new HashMap<Annotation, Integer>();
276     for(Annotation ann:mergeAnns.keySet()) {
277       String str = mergeAnns.get(ann);
278       int num=1;
279       while(str.contains("-")) {
280         ++num;
281         str = str.substring(str.indexOf('-')+1);
282       }
283       mergeAnnsNum.put(ann, new Integer(num));
284     }
285     //remove the annotaitons having the same places
286     for(Annotation ann:mergeAnnsNum.keySet()) {
287       Annotation annT=null;
288       int num0=-1;
289       Vector<Annotation>sameAnns= new Vector<Annotation>();
290       for(Annotation ann1:mergeAnnsNum.keySet()) {
291         if(ann.coextensive(ann1)) {
292           sameAnns.add(ann1);
293           int num = mergeAnnsNum.get(ann1).intValue();
294           if(num>num0) {
295             annT = ann1;
296             num0 = num;
297           }
298         }
299       //end the inner loop for merged annotations
300       //Keep the one which most annotators agree on.
301       sameAnns.remove(annT);
302       //Remove all others 
303       for(int i=0; i<sameAnns.size(); ++i)
304         mergeAnns.remove(sameAnns.elementAt(i));
305     }
306   
307   
308 
309   /**
310    * Check if the annotation sets contain the same annotations.
311    */
312   public static boolean isSameInstancesForAnnotators(AnnotationSet[] annsA, int vsy) {
313     int numAnnotators = annsA.length;
314     if(annsA[0== nullreturn false;
315     for(Annotation ann : annsA[0]) {
316       for(int iJud = 1; iJud < numAnnotators; ++iJud) {
317         if(annsA[iJud== nullreturn false;
318         boolean isContained = false;
319         for(Annotation ann1 : annsA[iJud]) {
320           // If the ann is not the same
321           if(ann.coextensive(ann1)) {
322             isContained = true;
323             break;
324           }
325         }
326         if(!isContained) {
327           if(vsy>0)
328           System.out.println("The " + iJud + " annotator cause different");
329           return false;
330         }
331       }// end of the loop for annotators
332     }// end of loop for each annotation in one document
333     // If the annotated instances are the same for every annotators.
334     return true;
335   }
336 }