pktools  2.6.3
Processing Kernel for geospatial data
ConfusionMatrix.cc
1 /**********************************************************************
2 ConfusionMatrix.cc: class for (classification accuracy) confusion matrix
3 Copyright (C) 2008-2012 Pieter Kempeneers
4 
5 This file is part of pktools
6 
7 pktools is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 pktools is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with pktools. If not, see <http://www.gnu.org/licenses/>.
19 ***********************************************************************/
20 #include "ConfusionMatrix.h"
21 #include <iostream>
22 #include <numeric>
23 
24 bool compareClass(const std::string& string1, const std::string& string2){
25  int int1=string2type<int>(string1);
26  int int2=string2type<int>(string2);
27  return(int1<int2);
28 };
29 
30 ConfusionMatrix::ConfusionMatrix()
31  : m_classes(),m_results()
32 {
33 }
34 
35 ConfusionMatrix::~ConfusionMatrix()
36 {
37 }
38 
39 //constructor where class names are 0,1,...,nclass-1
40 ConfusionMatrix::ConfusionMatrix(short nclass){
41  resize(nclass);
42 }
43 
44 ConfusionMatrix::ConfusionMatrix(const std::vector<std::string>& classNames){
45  setClassNames(classNames);
46 }
47 
48 //copy constructor
49 ConfusionMatrix::ConfusionMatrix(const ConfusionMatrix& cm){
50  setClassNames(cm.m_classes);
51  setResults(cm.m_results);
52 }
53 
54 //assignment operator
55 ConfusionMatrix& ConfusionMatrix::operator=(const ConfusionMatrix& cm){
56  //check for self-assignment by comparing the address of the implicit object and parameter
57  if(this==&cm)
58  return *this;
59  else{
60  setClassNames(cm.m_classes);
61  setResults(cm.m_results);
62  }
63  return *this;
64 }
65 
66 ConfusionMatrix& ConfusionMatrix::operator+=(const ConfusionMatrix &cm)
67 {
68  if(cm.m_classes.size()!=this->m_classes.size()){
69  std::cerr << "error0: "<< cm.m_classes.size() << "!=" << this->m_classes.size() << std::endl;
70  exit(0);
71  }
72  if(cm.m_results.size()!=this->m_results.size()){
73  std::cerr << "error1: "<< cm.m_results.size() << "!=" << this->m_results.size() << std::endl;
74  exit(1);
75  }
76  for(int irow=0;irow<m_results.size();++irow){
77  if(cm.m_results[irow].size()!=this->m_results[irow].size()){
78  std::cerr << "error2: " << cm.m_results[irow].size() << "!=" << this->m_results[irow].size() << std::endl;
79  exit(2);
80  }
81  for(int icol=0;icol<m_results[irow].size();++icol)
82  this->m_results[irow][icol]+=cm.m_results[irow][icol];
83  }
84  return *this;
85 }
86 
87 ConfusionMatrix& ConfusionMatrix::operator*=(double weight)
88 {
89  for(int irow=0;irow<m_results.size();++irow){
90  for(int icol=0;icol<m_results[irow].size();++icol)
91  m_results[irow][icol]*=weight;
92  }
93  return *this;
94 }
95 
96 void ConfusionMatrix::sortClassNames(){
97  sort(m_classes.begin(),m_classes.end(),compareClass);
98 }
99 
100 ConfusionMatrix ConfusionMatrix::operator*(double weight)
101 {
102  ConfusionMatrix result = *this;//make a copy of myself
103  result*=weight;
104  return result;
105 }
106 
107 void ConfusionMatrix::resize(short nclass){
108  m_classes.resize(nclass);
109  for(short iclass=0;iclass<nclass;++iclass){
110  std::ostringstream osclass;
111  osclass << iclass;
112  m_classes[iclass]=osclass.str();
113  }
114  m_results.resize(nclass,nclass);
115 }
116 
117 void ConfusionMatrix::setClassNames(const std::vector<std::string>& classNames, bool doSort){
118  m_classes=classNames;
119  if(doSort)
120  sortClassNames();
121  if(m_results.size()!=m_classes.size())
122  m_results.resize(m_classes.size(),m_classes.size());
123 }
124 
125 void ConfusionMatrix::pushBackClassName(const std::string& className, bool doSort){
126  m_classes.push_back(className);
127  if(doSort)
128  sortClassNames();
129  if(m_results.size()!=m_classes.size())
130  m_results.resize(m_classes.size(),m_classes.size());
131 }
132 
133 
134 void ConfusionMatrix::setResults(const Vector2d<double>& theResults){
135  m_results=theResults;
136 }
137 
138 void ConfusionMatrix::clearResults(){
139  m_results.clear();
140  m_results.resize(m_classes.size(),m_classes.size());
141 }
142 
143 void ConfusionMatrix::setResult(const std::string& theRef, const std::string& theClass, double theResult){
144  // int ir=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theRef));
145  // int ic=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theClass));
146  // assert(ir>=0);
147  // assert(ir<m_results.size());
148  // assert(ic>=0);
149  // assert(ic<m_results[ir].size());
150  int ir=getClassIndex(theRef);
151  int ic=getClassIndex(theClass);
152  m_results[ir][ic]=theResult;
153 }
154 
155 void ConfusionMatrix::incrementResult(const std::string& theRef, const std::string& theClass, double theIncrement){
156  // int ir=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theRef));
157  // int ic=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theClass));
158  int ir=getClassIndex(theRef);
159  int ic=getClassIndex(theClass);
160  assert(ir>=0);
161  if(ir>=m_results.size())
162  std::cerr << "Error: " << theRef << " not found in class ConfusionMatrix when incrementing for class " << theClass << std::endl;
163  assert(ir<m_results.size());
164  assert(ic>=0);
165  assert(ic<m_results[ir].size());
166  m_results[ir][ic]+=theIncrement;
167 }
168 
169 double ConfusionMatrix::nReference(const std::string& theRef) const{
170  // int ir=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theRef));
171  int ir=getClassIndex(theRef);
172  return accumulate(m_results[ir].begin(),m_results[ir].end(),0);
173 }
174 
175 double ConfusionMatrix::nReference() const{
176  double nref=0;
177  for(int ir=0;ir<m_classes.size();++ir)
178  nref+=accumulate(m_results[ir].begin(),m_results[ir].end(),0);
179  return nref;
180 }
181 
182 double ConfusionMatrix::nClassified(const std::string& theClass) const{
183  // int ic=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theClass));
184  int ic=getClassIndex(theClass);
185  double nclassified=0;
186  for(int iref=0;iref<m_results.size();++iref){
187  assert(ic<m_results[iref].size());
188  nclassified+=m_results[iref][ic];
189  }
190  return(nclassified);
191 }
192 
193 double ConfusionMatrix::pa(const std::string& theClass, double* se95) const{
194  assert(m_results.size());
195  assert(m_results.size()==m_classes.size());
196  double producer=0;
197  // int ir=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theClass));
198  int ir=getClassIndex(theClass);
199  assert(ir>=0);
200  assert(ir<m_results.size());
201  assert(!theClass.compare(m_classes[ir]));
202  for(int iclass=0;iclass<m_results.size();++iclass){
203  assert(iclass<m_results[ir].size());
204  producer+=m_results[ir][iclass];
205  }
206  double dpa=(producer>0)? static_cast<double>(m_results[ir][ir])/producer : 0;
207  double dqa=1.0-dpa;
208  if(se95!=NULL)
209  *se95=(dpa<1&&dpa>0)? sqrt(dpa*dqa/(producer-1)) : 0;
210  return dpa;
211 }
212 
213 int ConfusionMatrix::pa_pct(const std::string& theClass, double* se95) const{
214  double dpa=pa(theClass,se95);
215  if(se95!=NULL)
216  *se95=static_cast<double>(static_cast<int>(0.5+1000*(*se95)))/10.0;
217  return static_cast<int>(0.5+100.0*dpa);
218 }
219 
220 
221 double ConfusionMatrix::ua(const std::string& theClass, double* se95) const{
222  assert(m_results.size());
223  assert(m_results.size()==m_classes.size());
224  double user=0;
225  // int ic=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theClass));
226  int ic=getClassIndex(theClass);
227  assert(ic>=0);
228  assert(ic<m_results.size());
229  assert(!theClass.compare(m_classes[ic]));
230  for(int iref=0;iref<m_results.size();++iref){
231  assert(ic<m_results[iref].size());
232  user+=m_results[iref][ic];
233  }
234  double dua=(user>0)? static_cast<double>(m_results[ic][ic])/user : 0;
235  double dva=1.0-dva;
236  if(se95!=NULL)
237  *se95=(dua<1&&dua>0)? sqrt(dua*dva/(user-1)) : 0;
238  return dua;
239 }
240 
241 int ConfusionMatrix::ua_pct(const std::string& theClass,double* se95) const{
242  double dua=ua(theClass,se95);
243  if(se95!=NULL)
244  *se95=static_cast<double>(static_cast<int>(0.5+1000*(*se95)))/10.0;
245  return static_cast<int>(0.5+100.0*dua);
246 }
247 
248 double ConfusionMatrix::oa(double* se95) const{
249  double ntotal=m_results.sum();
250  double pChance=0;
251  double pCorrect=0;
252  for(int iclass=0;iclass<m_classes.size();++iclass)
253  pCorrect+=static_cast<double>(m_results[iclass][iclass])/ntotal;
254  double qCorrect=1-pCorrect;
255  if(se95!=NULL)
256  *se95=(pCorrect<1&&pCorrect>0)? sqrt(pCorrect*qCorrect/(ntotal-1)) : 0;
257  if(ntotal>0)
258  return(pCorrect);
259  else
260  return(0);
261 }
262 
263 int ConfusionMatrix::oa_pct(double* se95) const{
264  double doa=oa(se95);
265  if(se95!=NULL)
266  *se95=static_cast<double>(static_cast<int>(0.5+1000*(*se95)))/10.0;
267  return static_cast<int>(0.5+100.0*doa);
268 }
269 
270 double ConfusionMatrix::kappa() const{
271  double ntotal=m_results.sum();
272  double pChance=0;
273  double pCorrect=0;
274  for(int iclass=0;iclass<m_classes.size();++iclass){
275  pChance+=nClassified(m_classes[iclass])*nReference(m_classes[iclass])/ntotal/ntotal;
276  pCorrect+=static_cast<double>(m_results[iclass][iclass])/ntotal;
277  }
278  if(pChance<1)
279  return((pCorrect-pChance)/(1-pChance));
280  else
281  return(0);
282 }