pktools 2.6.7
Processing Kernel for geospatial data
ConfusionMatrix.cc
1/**********************************************************************
2ConfusionMatrix.cc: class for (classification accuracy) confusion matrix
3Copyright (C) 2008-2012 Pieter Kempeneers
4
5This file is part of pktools
6
7pktools is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or
10(at your option) any later version.
11
12pktools is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with pktools. If not, see <http://www.gnu.org/licenses/>.
19***********************************************************************/
20#include "ConfusionMatrix.h"
21#include <iostream>
22#include <numeric>
23
24using namespace confusionmatrix;
25
26bool 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
32ConfusionMatrix::ConfusionMatrix()
33 : m_classes(),m_results(),m_se95(true),m_format(ASCII)
34{
35}
36
37ConfusionMatrix::~ConfusionMatrix()
38{
39}
40
41//constructor where class names are 0,1,...,nclass-1
42ConfusionMatrix::ConfusionMatrix(short nclass){
43 resize(nclass);
44}
45
46ConfusionMatrix::ConfusionMatrix(const std::vector<std::string>& classNames){
47 setClassNames(classNames);
48}
49
50//copy constructor
51ConfusionMatrix::ConfusionMatrix(const ConfusionMatrix& cm){
52 setClassNames(cm.m_classes);
53 setResults(cm.m_results);
54}
55
56//assignment operator
57ConfusionMatrix& 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
68ConfusionMatrix& 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
89ConfusionMatrix& 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
98void ConfusionMatrix::sortClassNames(){
99 sort(m_classes.begin(),m_classes.end(),compareClass);
100}
101
102ConfusionMatrix ConfusionMatrix::operator*(double weight)
103{
104 ConfusionMatrix result = *this;//make a copy of myself
105 result*=weight;
106 return result;
107}
108
109void 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
119void 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
127void 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
136void ConfusionMatrix::setResults(const Vector2d<double>& theResults){
137 m_results=theResults;
138}
139
140void ConfusionMatrix::clearResults(){
141 m_results.clear();
142 m_results.resize(m_classes.size(),m_classes.size());
143}
144
145void 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
157void 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
171double 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
177double 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
184double 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
195double 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
215int 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
223double 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
243int 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
250double 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
265int 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
272double 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}