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 */
030package org.openimaj.text.nlp.sentiment.type;
031
032import java.util.HashMap;
033import java.util.Map;
034
035import org.openimaj.util.math.ObjectArithmetic;
036import org.openimaj.util.math.ScalarArithmetic;
037
038/**
039 * A Discrete count bipolar sentiment is one which is positive, negative or netural by some counts.
040 * 
041 * The sentiment holds values for counts of words considered to be one of the three and the total number of words the sentiment was decided
042 * against. 
043 * 
044 * An assumption is made that a single term can only have a single sentiment, therefore
045 * positive + negative + netural < total
046 * though perhaps not equal as some terms may be none of the 3 (stop words etc.)
047 * 
048 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
049 *
050 */
051public class DiscreteCountBipolarSentiment implements Sentiment, BipolarSentimentProvider, WeightedBipolarSentimentProvider, ScalarArithmetic<DiscreteCountBipolarSentiment, Integer>, ObjectArithmetic<DiscreteCountBipolarSentiment>{
052        
053        /**
054         * A single positive count
055         */
056        public static final DiscreteCountBipolarSentiment POSITIVE = new DiscreteCountBipolarSentiment(1,0,0);
057        /**
058         * a single negative count
059         */
060        public static final DiscreteCountBipolarSentiment NEGATIVE = new DiscreteCountBipolarSentiment(0,1,0);
061        /**
062         * a single neutral count
063         */
064        public static final DiscreteCountBipolarSentiment NEUTRAL = new DiscreteCountBipolarSentiment(0,0,1);
065        private int positive,negative,neutral;
066        private int total;
067        
068        /**
069         * all weights set to 0
070         */
071        public DiscreteCountBipolarSentiment() {}
072        /**
073         * @param positive the positive 
074         * @param negative the negative
075         * @param neutral the neutral
076         * @param total the total number of terms
077         * @throws InvalidSentimentException 
078         */
079        public DiscreteCountBipolarSentiment(int positive, int negative, int neutral, int total) throws InvalidSentimentException {
080                this.positive = positive;
081                this.negative = negative;
082                this.neutral = neutral;
083                this.total = total;
084                if(positive + neutral + negative > total){
085                        throw new InvalidSentimentException("total counts was less than the total of positive, negative and neutral, this is impossible");
086                }
087        }
088        
089        /**
090         * @param positive the positive 
091         * @param negative the negative
092         * @param neutral the neutral 
093         */
094        public DiscreteCountBipolarSentiment(int positive, int negative, int neutral){
095                this.positive = positive;
096                this.negative = negative;
097                this.neutral = neutral;
098                this.total = positive + negative + neutral;
099        }
100        
101        /**
102         * @return positive
103         */
104        public int positive(){
105                return positive;
106        }
107        
108        /**
109         * @return negative
110         */
111        public int negative(){
112                return negative;
113        }
114        
115        /**
116         * @return neutral
117         */
118        public int neutral(){
119                return neutral;
120        }
121        
122        @Override
123        public BipolarSentiment bipolar() {
124                if(this.positive > this.negative){
125                        if(this.positive > this.neutral){
126                                return BipolarSentiment.POSITIVE;
127                        }
128                        else{
129                                return BipolarSentiment.NEUTRAL;
130                        }
131                }
132                else{
133                        if(this.negative > this.neutral){
134                                return BipolarSentiment.NEGATIVE;
135                        }
136                        else{
137                                return BipolarSentiment.NEUTRAL;
138                        }
139                }
140        }
141        @Override
142        public BipolarSentiment bipolar(double deltaThresh) {
143                if(this.positive > this.negative  * deltaThresh){
144                        if(this.positive > this.neutral * deltaThresh){
145                                return BipolarSentiment.POSITIVE;
146                        }
147                        else if(this.neutral > this.positive  * deltaThresh){
148                                return BipolarSentiment.NEUTRAL;
149                        }
150                }
151                else{
152                        if(this.negative > this.neutral  * deltaThresh){
153                                return BipolarSentiment.NEGATIVE;
154                        }
155                        else if(this.neutral > this.negative *  deltaThresh){
156                                return BipolarSentiment.NEUTRAL;
157                        }
158                }
159                return null;
160        }
161
162        @Override
163        public Map<String, ?> asMap() {
164                HashMap<String, Integer> ret = new HashMap<String,Integer>();
165                ret.put("positive", positive);
166                ret.put("negative", negative);
167                ret.put("neutral", neutral);
168                ret.put("total", total);
169                return ret;
170        }
171
172        @Override
173        public void fromMap(Map<String, ?> map) throws UnrecognisedMapException {
174                if(!map.containsKey("positive") || !map.containsKey("negative") || !map.containsKey("neutral") ){
175                        throw new UnrecognisedMapException("positive","negative","neutral");
176                }
177                this.positive = (Integer) map.get("positive");
178                this.negative = (Integer) map.get("negative");
179                this.neutral = (Integer) map.get("neutral");
180                this.total = (Integer) map.get("total");
181        }
182        @Override
183        public WeightedBipolarSentiment weightedBipolar() {
184                return new WeightedBipolarSentiment(
185                        this.positive / (double)this.total,
186                        this.negative / (double)this.total,
187                        this.neutral  / (double)this.total
188                );
189        }
190        
191        @Override
192        protected DiscreteCountBipolarSentiment clone() {
193                try {
194                        return new DiscreteCountBipolarSentiment(this.positive, this.negative,this.neutral, this.total);
195                } catch (InvalidSentimentException e) {
196                        return null;
197                }
198        }
199        
200        @Override
201        public DiscreteCountBipolarSentiment addInplace(DiscreteCountBipolarSentiment s) {
202                this.positive += s.positive;
203                this.negative += s.negative;
204                this.neutral += s.neutral;
205                return this;
206        }
207        @Override
208        public DiscreteCountBipolarSentiment multiplyInplace(DiscreteCountBipolarSentiment s) {
209                this.positive *= s.positive;
210                this.negative *= s.negative;
211                this.neutral *= s.neutral;
212                return this;
213        }
214        @Override
215        public DiscreteCountBipolarSentiment divideInplace(DiscreteCountBipolarSentiment s) {
216                this.positive /= s.positive;
217                this.negative /= s.negative;
218                this.neutral /= s.neutral;
219                return this;
220        }
221        @Override
222        public DiscreteCountBipolarSentiment add(DiscreteCountBipolarSentiment s) {
223                DiscreteCountBipolarSentiment c = this.clone();
224                return c.addInplace(s);
225        }
226        @Override
227        public DiscreteCountBipolarSentiment subtract(DiscreteCountBipolarSentiment s) {
228                return this.add(s.multiply(-1));
229        }
230        @Override
231        public DiscreteCountBipolarSentiment subtractInplace(DiscreteCountBipolarSentiment s) {
232                return this.addInplace(s.multiply(-1));
233        }
234        @Override
235        public DiscreteCountBipolarSentiment multiply(DiscreteCountBipolarSentiment s) {
236                return this.clone().multiplyInplace(s);
237        }
238        @Override
239        public DiscreteCountBipolarSentiment divide(DiscreteCountBipolarSentiment s) {
240                return this.clone().divideInplace(s);
241        }
242        
243        @Override
244        public DiscreteCountBipolarSentiment addInplace(Integer s) {
245                this.positive += s;
246                this.negative += s;
247                this.neutral += s;
248                return this;
249        }
250        @Override
251        public DiscreteCountBipolarSentiment multiplyInplace(Integer  s) {
252                this.positive *= s;
253                this.negative *= s;
254                this.neutral *= s;
255                return this;
256        }
257        @Override
258        public DiscreteCountBipolarSentiment divideInplace(Integer  s) {
259                this.positive /= s;
260                this.negative /= s;
261                this.neutral /= s;
262                return this;
263        }
264        @Override
265        public DiscreteCountBipolarSentiment add(Integer  s) {
266                DiscreteCountBipolarSentiment c = this.clone();
267                return c.addInplace(s);
268        }
269        @Override
270        public DiscreteCountBipolarSentiment subtract(Integer  s) {
271                return this.add(s * -1);
272        }
273        @Override
274        public DiscreteCountBipolarSentiment subtractInplace(Integer  s) {
275                return this.addInplace(s * -1);
276        }
277        @Override
278        public DiscreteCountBipolarSentiment multiply(Integer s) {
279                return this.clone().multiplyInplace(s);
280        }
281        @Override
282        public DiscreteCountBipolarSentiment divide(Integer s) {
283                return this.clone().divideInplace(s);
284        }
285        
286        @Override
287        public String toString() {
288                String frmt = "Positive == %d\nNegative == %d\nNetural == %d";
289                return String.format(frmt,positive,negative,neutral);
290        }
291
292}