001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030/**
031 * 
032 */
033package org.openimaj.experiment.evaluation.agreement;
034
035import java.util.HashMap;
036import java.util.List;
037import java.util.Map;
038import java.util.Map.Entry;
039
040import org.openimaj.util.pair.ObjectFloatPair;
041
042/**
043 * Majority voting.
044 * 
045 * @author David Dupplaw (dpd@ecs.soton.ac.uk)
046 * @created 14 Aug 2013
047 * @version $Author$, $Revision$, $Date$
048 */
049public class MajorityVoting
050{
051        /**
052         * Returns the basic majority vote or null if there is no majority within
053         * the given list of annotations.
054         * 
055         * @param data
056         *            The list of annotations to score
057         * @return The majority annotation or NULL if there is no majority
058         */
059        public static <A> ObjectFloatPair<A> calculateBasicMajorityVote(
060                        List<A> data)
061        {
062                // Count all the different annotations.
063                final HashMap<A, Integer> count = new HashMap<A, Integer>();
064                for (final A s : data)
065                {
066                        if (count.get(s) == null)
067                                count.put(s, 1);
068                        else
069                                count.put(s, count.get(s) + 1);
070                }
071
072                // Find the maximum annotation
073                A majority = null;
074                int max = 0;
075                for (final Entry<A, Integer> x : count.entrySet())
076                {
077                        // If we found a new max, we have a majority
078                        if (x.getValue() > max)
079                        {
080                                max = x.getValue();
081                                majority = x.getKey();
082                        }
083                        else
084                        // If we found another one with the same count as max
085                        // then we no longer have a majority
086                        if (x.getValue() == max)
087                        {
088                                majority = null;
089                                // We mustn't reset max.
090                        }
091                }
092
093                // We'll return null if there's no majority
094                if (majority == null)
095                        return null;
096
097                // Otherwise we return the majority annotation
098                return new ObjectFloatPair<A>(majority,
099                                count.get(majority) / (float) data.size());
100        }
101
102        /**
103         * Calculated the majority vote for a set of subjects, where each subject
104         * has a set of answers. Note that as the majority voting method may return
105         * null in the case of no majority, it is possible that some of the subjects
106         * in the return of this method will have a null map.
107         * 
108         * @param data
109         *            The data
110         * @return The subjects mapped to their majority vote.
111         */
112        public static <A> Map<String, ObjectFloatPair<A>>
113                        calculateBasicMajorityVote(Map<String, List<A>> data)
114        {
115                final Map<String, ObjectFloatPair<A>> out =
116                                new HashMap<String, ObjectFloatPair<A>>();
117
118                for (final Entry<String, List<A>> x : data.entrySet())
119                        out.put(x.getKey(), calculateBasicMajorityVote(x.getValue()));
120
121                return out;
122        }
123}