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}