pktools  2.6.7
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 using namespace confusionmatrix;
25 
26 bool compareClass(const std::string& string1, const std::string& string2){
27  int int1=string2type<int>(string1);
28  int int2=string2type<int>(string2);
29  return(int1<int2);
30 };
31 
32 ConfusionMatrix::ConfusionMatrix()
33  : m_classes(),m_results(),m_se95(true),m_format(ASCII)
34 {
35 }
36 
37 ConfusionMatrix::~ConfusionMatrix()
38 {
39 }
40 
41 //constructor where class names are 0,1,...,nclass-1
42 ConfusionMatrix::ConfusionMatrix(short nclass){
43  resize(nclass);
44 }
45 
46 ConfusionMatrix::ConfusionMatrix(const std::vector<std::string>& classNames){
47  setClassNames(classNames);
48 }
49 
50 //copy constructor
51 ConfusionMatrix::ConfusionMatrix(const ConfusionMatrix& cm){
52  setClassNames(cm.m_classes);
53  setResults(cm.m_results);
54 }
55 
56 //assignment operator
57 ConfusionMatrix& ConfusionMatrix::operator=(const ConfusionMatrix& cm){
58  //check for self-assignment by comparing the address of the implicit object and parameter
59  if(this==&cm)
60  return *this;
61  else{
62  setClassNames(cm.m_classes);
63  setResults(cm.m_results);
64  }
65  return *this;
66 }
67 
68 ConfusionMatrix& ConfusionMatrix::operator+=(const ConfusionMatrix &cm)
69 {
70  if(cm.m_classes.size()!=this->m_classes.size()){
71  std::cerr << "error0: "<< cm.m_classes.size() << "!=" << this->m_classes.size() << std::endl;
72  exit(0);
73  }
74  if(cm.m_results.size()!=this->m_results.size()){
75  std::cerr << "error1: "<< cm.m_results.size() << "!=" << this->m_results.size() << std::endl;
76  exit(1);
77  }
78  for(int irow=0;irow<m_results.size();++irow){
79  if(cm.m_results[irow].size()!=this->m_results[irow].size()){
80  std::cerr << "error2: " << cm.m_results[irow].size() << "!=" << this->m_results[irow].size() << std::endl;
81  exit(2);
82  }
83  for(int icol=0;icol<m_results[irow].size();++icol)
84  this->m_results[irow][icol]+=cm.m_results[irow][icol];
85  }
86  return *this;
87 }
88 
89 ConfusionMatrix& ConfusionMatrix::operator*=(double weight)
90 {
91  for(int irow=0;irow<m_results.size();++irow){
92  for(int icol=0;icol<m_results[irow].size();++icol)
93  m_results[irow][icol]*=weight;
94  }
95  return *this;
96 }
97 
98 void ConfusionMatrix::sortClassNames(){
99  sort(m_classes.begin(),m_classes.end(),compareClass);
100 }
101 
102 ConfusionMatrix ConfusionMatrix::operator*(double weight)
103 {
104  ConfusionMatrix result = *this;//make a copy of myself
105  result*=weight;
106  return result;
107 }
108 
109 void ConfusionMatrix::resize(short nclass){
110  m_classes.resize(nclass);
111  for(short iclass=0;iclass<nclass;++iclass){
112  std::ostringstream osclass;
113  osclass << iclass;
114  m_classes[iclass]=osclass.str();
115  }
116  m_results.resize(nclass,nclass);
117 }
118 
119 void ConfusionMatrix::setClassNames(const std::vector<std::string>& classNames, bool doSort){
120  m_classes=classNames;
121  if(doSort)
122  sortClassNames();
123  if(m_results.size()!=m_classes.size())
124  m_results.resize(m_classes.size(),m_classes.size());
125 }
126 
127 void ConfusionMatrix::pushBackClassName(const std::string& className, bool doSort){
128  m_classes.push_back(className);
129  if(doSort)
130  sortClassNames();
131  if(m_results.size()!=m_classes.size())
132  m_results.resize(m_classes.size(),m_classes.size());
133 }
134 
135 
136 void ConfusionMatrix::setResults(const Vector2d<double>& theResults){
137  m_results=theResults;
138 }
139 
140 void ConfusionMatrix::clearResults(){
141  m_results.clear();
142  m_results.resize(m_classes.size(),m_classes.size());
143 }
144 
145 void ConfusionMatrix::setResult(const std::string& theRef, const std::string& theClass, double theResult){
146  // int ir=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theRef));
147  // int ic=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theClass));
148  // assert(ir>=0);
149  // assert(ir<m_results.size());
150  // assert(ic>=0);
151  // assert(ic<m_results[ir].size());
152  int ir=getClassIndex(theRef);
153  int ic=getClassIndex(theClass);
154  m_results[ir][ic]=theResult;
155 }
156 
157 void ConfusionMatrix::incrementResult(const std::string& theRef, const std::string& theClass, double theIncrement){
158  // int ir=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theRef));
159  // int ic=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theClass));
160  int ir=getClassIndex(theRef);
161  int ic=getClassIndex(theClass);
162  assert(ir>=0);
163  if(ir>=m_results.size())
164  std::cerr << "Error: " << theRef << " not found in class ConfusionMatrix when incrementing for class " << theClass << std::endl;
165  assert(ir<m_results.size());
166  assert(ic>=0);
167  assert(ic<m_results[ir].size());
168  m_results[ir][ic]+=theIncrement;
169 }
170 
171 double ConfusionMatrix::nReference(const std::string& theRef) const{
172  // int ir=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theRef));
173  int ir=getClassIndex(theRef);
174  return accumulate(m_results[ir].begin(),m_results[ir].end(),0);
175 }
176 
177 double ConfusionMatrix::nReference() const{
178  double nref=0;
179  for(int ir=0;ir<m_classes.size();++ir)
180  nref+=accumulate(m_results[ir].begin(),m_results[ir].end(),0);
181  return nref;
182 }
183 
184 double ConfusionMatrix::nClassified(const std::string& theClass) const{
185  // int ic=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theClass));
186  int ic=getClassIndex(theClass);
187  double nclassified=0;
188  for(int iref=0;iref<m_results.size();++iref){
189  assert(ic<m_results[iref].size());
190  nclassified+=m_results[iref][ic];
191  }
192  return(nclassified);
193 }
194 
195 double ConfusionMatrix::pa(const std::string& theClass, double* se95) const{
196  assert(m_results.size());
197  assert(m_results.size()==m_classes.size());
198  double producer=0;
199  // int ir=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theClass));
200  int ir=getClassIndex(theClass);
201  assert(ir>=0);
202  assert(ir<m_results.size());
203  assert(!theClass.compare(m_classes[ir]));
204  for(int iclass=0;iclass<m_results.size();++iclass){
205  assert(iclass<m_results[ir].size());
206  producer+=m_results[ir][iclass];
207  }
208  double dpa=(producer>0)? static_cast<double>(m_results[ir][ir])/producer : 0;
209  double dqa=1.0-dpa;
210  if(se95!=NULL)
211  *se95=(dpa<1&&dpa>0)? sqrt(dpa*dqa/(producer-1)) : 0;
212  return dpa;
213 }
214 
215 int ConfusionMatrix::pa_pct(const std::string& theClass, double* se95) const{
216  double dpa=pa(theClass,se95);
217  if(se95!=NULL)
218  *se95=static_cast<double>(static_cast<int>(0.5+1000*(*se95)))/10.0;
219  return static_cast<int>(0.5+100.0*dpa);
220 }
221 
222 
223 double ConfusionMatrix::ua(const std::string& theClass, double* se95) const{
224  assert(m_results.size());
225  assert(m_results.size()==m_classes.size());
226  double user=0;
227  // int ic=distance(m_classes.begin(),find(m_classes.begin(),m_classes.end(),theClass));
228  int ic=getClassIndex(theClass);
229  assert(ic>=0);
230  assert(ic<m_results.size());
231  assert(!theClass.compare(m_classes[ic]));
232  for(int iref=0;iref<m_results.size();++iref){
233  assert(ic<m_results[iref].size());
234  user+=m_results[iref][ic];
235  }
236  double dua=(user>0)? static_cast<double>(m_results[ic][ic])/user : 0;
237  double dva=1.0-dva;
238  if(se95!=NULL)
239  *se95=(dua<1&&dua>0)? sqrt(dua*dva/(user-1)) : 0;
240  return dua;
241 }
242 
243 int ConfusionMatrix::ua_pct(const std::string& theClass,double* se95) const{
244  double dua=ua(theClass,se95);
245  if(se95!=NULL)
246  *se95=static_cast<double>(static_cast<int>(0.5+1000*(*se95)))/10.0;
247  return static_cast<int>(0.5+100.0*dua);
248 }
249 
250 double ConfusionMatrix::oa(double* se95) const{
251  double ntotal=m_results.sum();
252  double pChance=0;
253  double pCorrect=0;
254  for(int iclass=0;iclass<m_classes.size();++iclass)
255  pCorrect+=static_cast<double>(m_results[iclass][iclass])/ntotal;
256  double qCorrect=1-pCorrect;
257  if(se95!=NULL)
258  *se95=(pCorrect<1&&pCorrect>0)? sqrt(pCorrect*qCorrect/(ntotal-1)) : 0;
259  if(ntotal>0)
260  return(pCorrect);
261  else
262  return(0);
263 }
264 
265 int ConfusionMatrix::oa_pct(double* se95) const{
266  double doa=oa(se95);
267  if(se95!=NULL)
268  *se95=static_cast<double>(static_cast<int>(0.5+1000*(*se95)))/10.0;
269  return static_cast<int>(0.5+100.0*doa);
270 }
271 
272 double ConfusionMatrix::kappa() const{
273  double ntotal=m_results.sum();
274  double pChance=0;
275  double pCorrect=0;
276  for(int iclass=0;iclass<m_classes.size();++iclass){
277  pChance+=nClassified(m_classes[iclass])*nReference(m_classes[iclass])/ntotal/ntotal;
278  pCorrect+=static_cast<double>(m_results[iclass][iclass])/ntotal;
279  }
280  if(pChance<1)
281  return((pCorrect-pChance)/(1-pChance));
282  else
283  return(0);
284 }