contrib/mul/mbl/mbl_table.cxx
Go to the documentation of this file.
00001 #include "mbl_table.h"
00002 //:
00003 // \file
00004 // \author Kevin de Souza
00005 // \date 05-Aug-2004
00006 // \brief Container for tabulated data suitable for reading/writing to delimited text files
00007 
00008 #include <vcl_cstdlib.h>
00009 #include <vcl_iostream.h>
00010 #include <vcl_cmath.h>
00011 
00012 
00013 // Tolerance used to determine whether table entries are equal
00014 static double tolerance_ = 1e-15;
00015 
00016 // Whether the tolerance is applied as a fractional difference
00017 static bool fractional_tolerance_ = false;
00018 
00019 // Level of verbosity used for error output.
00020 static int verbosity_ = 0;
00021 
00022 
00023 //========================================================================
00024 // Constructor
00025 //========================================================================
00026 mbl_table::mbl_table(const char delim)
00027   : delimiter_(delim)
00028 {
00029 }
00030 
00031 
00032 //========================================================================
00033 //: Constructor
00034 //========================================================================
00035 mbl_table::mbl_table(const char delim,
00036                      const vcl_vector<vcl_string>& headers)
00037   : delimiter_(delim),
00038     column_headers_(headers)
00039 {
00040   unsigned ncols = column_headers_.size();
00041 
00042   // Allocate column vectors
00043   columns_.resize(ncols);
00044 
00045   // Map each column header string to column index
00046   for (unsigned int c=0; c<ncols; ++c)
00047   {
00048     header_to_column_index_[headers[c]] = c;
00049   }
00050 }
00051 
00052 
00053 //========================================================================
00054 // Return the number of columns
00055 //========================================================================
00056 unsigned mbl_table::num_cols() const
00057 {
00058   return columns_.size();
00059 }
00060 
00061 
00062 //========================================================================
00063 // Return the number of rows
00064 //========================================================================
00065 unsigned mbl_table::num_rows() const
00066 {
00067     int nrows=0;
00068     // If there are at least 1 column, assume all columns are the same as the first
00069     if (columns_.size()>0) nrows=columns_[0].size();
00070     return nrows;
00071 }
00072 
00073 
00074 //========================================================================
00075 // Returns true if column exists
00076 //========================================================================
00077 bool mbl_table::column_exists(const vcl_string& header) const
00078 {
00079     // Does the map contain this header?
00080     vcl_map<vcl_string, unsigned>::const_iterator iter =
00081         header_to_column_index_.find(header);
00082 
00083     return  iter != header_to_column_index_.end();
00084 }
00085 
00086 //========================================================================
00087 // Get the column of data corresponding to a particular heading.
00088 //========================================================================
00089 bool mbl_table::get_column(const vcl_string& header,
00090                            vcl_vector<double>& column) const
00091 {
00092   bool success = false;
00093   column.clear();
00094 
00095   // Does the map contain this header?
00096   vcl_map<vcl_string, unsigned>::const_iterator iter =
00097     header_to_column_index_.find(header);
00098 
00099   if (iter != header_to_column_index_.end())
00100   {
00101     // Get the corresponding column vector and copy it to the output vector
00102     column = columns_[iter->second];
00103     success = true;
00104   }
00105   else
00106   {
00107     vcl_cerr << "ERROR: mbl_table::get_column(): column \""
00108              << header << "\" does not exist in the table.\n";
00109   }
00110 
00111   return success;
00112 }
00113 
00114 
00115 //========================================================================
00116 // Get a specified row of data.
00117 //========================================================================
00118 bool mbl_table::get_row(const unsigned& r,
00119                         vcl_vector<double>& row) const
00120 {
00121   // Clear output data
00122   row.clear();
00123 
00124   // Check that the specified row index is valid
00125   if (r < num_rows())
00126   {
00127     unsigned int ncols = num_cols();
00128     row.resize(ncols);
00129     for (unsigned c=0; c<ncols; ++c)
00130     {
00131       row[c] = columns_[c][r];
00132     }
00133     return true;
00134   }
00135   else
00136   {
00137     vcl_cerr << "ERROR: mbl_table::get_row(): row "
00138              << r << " does not exist in the table.\n";
00139     return false;
00140   }
00141 }
00142 
00143 
00144 //========================================================================
00145 // Get the list of column headers (in column order).
00146 //========================================================================
00147 void mbl_table::get_column_headers(vcl_vector<vcl_string>& headers) const
00148 {
00149   headers = column_headers_;
00150 }
00151 
00152 
00153 //========================================================================
00154 // Set the value of an existing element.
00155 //========================================================================
00156 bool mbl_table::set_element(const vcl_string& header,
00157                             const unsigned r,
00158                             const double value)
00159 {
00160   bool success = false;
00161 
00162   // Does the map contain this column header?
00163   vcl_map<vcl_string, unsigned>::const_iterator iter =
00164     header_to_column_index_.find(header);
00165 
00166   if (iter != header_to_column_index_.end())
00167   {
00168     // Does the column have sufficient rows?
00169     vcl_vector<double>& col = columns_[iter->second];
00170     if (col.size()>r)
00171     {
00172       // Set the value
00173       col[r] = value;
00174       success = true;
00175     }
00176     else
00177     {
00178       vcl_cerr << "ERROR: mbl_table::set_element(): row "
00179                << r << " does not exist in the table.\n";
00180     }
00181   }
00182   else
00183   {
00184     vcl_cerr << "ERROR: mbl_table::set_element(): column \""
00185              << header << "\" does not exist in the table.\n";
00186   }
00187 
00188   return success;
00189 }
00190 
00191 
00192 //========================================================================
00193 // Get the value of an existing element.
00194 //========================================================================
00195 double mbl_table::get_element(const vcl_string& header,
00196                               const unsigned r,
00197                               bool* success/*=0*/) const
00198 {
00199   double value = 1e-19;
00200   if (success)
00201     *success = false;
00202 
00203   // Does the map contain this column header?
00204   vcl_map<vcl_string, unsigned>::const_iterator iter =
00205     header_to_column_index_.find(header);
00206 
00207   if (iter != header_to_column_index_.end())
00208   {
00209     // Does the column have sufficient rows?
00210     const vcl_vector<double>& col = columns_[iter->second];
00211     if (col.size()>r)
00212     {
00213       // Get the value
00214       value = col[r];
00215       if (success)
00216         *success = true;
00217     }
00218     else
00219     {
00220       vcl_cerr << "ERROR: mbl_table::get_element(): row "
00221                << r << " does not exist in the table.\n";
00222     }
00223   }
00224   else
00225   {
00226     vcl_cerr << "ERROR: mbl_table::get_element(): column \""
00227              << header << "\" does not exist in the table.\n";
00228   }
00229 
00230   return value;
00231 }
00232 
00233 
00234 //========================================================================
00235 // Append a column of data with its own heading.
00236 //========================================================================
00237 bool mbl_table::append_column(const vcl_string& header,
00238                               const vcl_vector<double>& column)
00239 {
00240   // Check whether there is already a column with this heading
00241   if (header_to_column_index_.find(header) == header_to_column_index_.end())
00242   {
00243     // Check that the length of the new column matches the existing columns
00244     if (num_rows()==0 || num_rows()==column.size())
00245     {
00246       column_headers_.push_back(header);
00247       columns_.push_back(column);
00248       header_to_column_index_[header] = columns_.size()-1;
00249       return true;
00250     }
00251     else
00252     {
00253       vcl_cerr << "ERROR: mbl_table::append_column(): "
00254                << "new column is different length from existing columns.\n"
00255                << "Column not appended.\n";
00256       return false;
00257     }
00258   }
00259   else
00260   {
00261     vcl_cerr << "ERROR: mbl_table::append_column(): a column with header \""
00262              << header << "\" already exists.\n"
00263              << "Column not appended.\n";
00264     return false;
00265   }
00266 }
00267 
00268 
00269 //========================================================================
00270 //: Append an empty column with its own heading.
00271 //========================================================================
00272 bool mbl_table::append_column(const vcl_string& header,
00273                               const double val/*=0*/)
00274 {
00275   // Check whether there is already a column with this heading
00276   if (header_to_column_index_.find(header) == header_to_column_index_.end())
00277   {
00278     // Append a new column of the same length as existing columns
00279     column_headers_.push_back(header);
00280     unsigned c = columns_.size();
00281     header_to_column_index_[header] = c;
00282     columns_.push_back(vcl_vector<double>());
00283     columns_[c].resize(num_rows(), val);
00284     return true;
00285   }
00286   else
00287   {
00288     vcl_cerr << "ERROR: mbl_table::append_column(): a column with header \""
00289              << header << "\" already exists.\n"
00290              << "Column not appended.\n";
00291     return false;
00292   }
00293 }
00294 
00295 
00296 //========================================================================
00297 // Append a row of data.
00298 //========================================================================
00299 bool mbl_table::append_row(const vcl_vector<double>& row)
00300 {
00301   // Check that the length of the new row matches the existing rows
00302   unsigned ncols = num_cols();
00303   if (ncols==row.size())
00304   {
00305     for (unsigned c=0; c<ncols; ++c)
00306     {
00307       columns_[c].push_back(row[c]);
00308     }
00309     return true;
00310   }
00311   else
00312   {
00313     vcl_cerr << "ERROR: mbl_table::append_row(): "
00314              << "new row is different length from existing row.\n"
00315              << "Row not appended.\n";
00316     return false;
00317   }
00318 }
00319 
00320 
00321 //========================================================================
00322 // Append an empty row.
00323 //========================================================================
00324 bool mbl_table::append_row(const double val/*=0*/)
00325 {
00326   // Append a new element to each column
00327   unsigned ncols = num_cols();
00328   for (unsigned c=0; c<ncols; ++c)
00329   {
00330     columns_[c].push_back(val);
00331   }
00332 
00333   return true;
00334 }
00335 
00336 
00337 //========================================================================
00338 // Load this table's data from specified text stream.
00339 //========================================================================
00340 bool mbl_table::read(vcl_istream& is)
00341 {
00342   bool success = false;
00343 
00344   if (is.good() && !is.eof())
00345   {
00346     // Read header row
00347     bool eol = false;
00348     bool eof = false;
00349     unsigned col = 0;
00350     while (!eol && !eof)
00351     {
00352       vcl_string str;
00353       if (read_delimited_string(is, str, eol, eof))
00354       {
00355         // Create an empty column vector and enter it into the map
00356         column_headers_.push_back(str);
00357         header_to_column_index_[str] = col;
00358         columns_.push_back(vcl_vector<double>(0));
00359 
00360         col++;
00361       }
00362     }
00363 
00364     // Read table data
00365     while (!is.eof())
00366     {
00367       // Read one row
00368       eol = false;
00369       eof = false;
00370       unsigned col = 0;
00371 
00372       while (!eol && !eof)
00373       {
00374         vcl_string str;
00375         if (read_delimited_string(is, str, eol, eof))
00376         {
00377           // Convert string to double (NB sets to 0 if string is non-numeric)
00378           double val = vcl_atof(str.c_str());
00379 
00380           // Add this double value to the current column vector
00381           columns_[col].push_back(val);
00382 
00383           // Advance to the next column or back to the first column
00384           col++;
00385           if (col==num_cols()) col = 0;
00386         }
00387       }
00388     }
00389 
00390     success = true;
00391   }
00392 
00393   return success;
00394 }
00395 
00396 
00397 //========================================================================
00398 // Save this table's data to specified text stream.
00399 //========================================================================
00400 void mbl_table::write(vcl_ostream& os) const
00401 {
00402   // How many columns are there?
00403   unsigned int ncols = num_cols();
00404 
00405   // How many rows of data do we expect?
00406   unsigned int nrows = num_rows();
00407 
00408   // Write column headers row
00409   for (unsigned c=0; c<ncols; ++c)
00410   {
00411     os << column_headers_[c] << delimiter_;
00412   }
00413   os << '\n';
00414 
00415   // Write data rows
00416   for (unsigned r=0; r<nrows; ++r)
00417   {
00418     for (unsigned c=0; c<ncols; ++c)
00419     {
00420       os << columns_[c][r] << delimiter_;
00421     }
00422     os << '\n';
00423   }
00424 }
00425 //========================================================================
00426 //: Create a new table with as subset of columns defined by headers
00427 //========================================================================
00428 bool mbl_table::subtable(mbl_table &new_table,  const vcl_vector<vcl_string> &headers) const
00429 {
00430     bool ret = true;
00431 
00432     new_table = mbl_table();
00433 
00434 
00435     // Write column headers row
00436     for (unsigned c=0; c<headers.size(); ++c)
00437     {
00438         // get the column for the header if available
00439         vcl_map<vcl_string, unsigned>::const_iterator iter =
00440             header_to_column_index_.find(headers[c]);
00441 
00442         if (iter != header_to_column_index_.end())
00443         {
00444             new_table.append_column(headers[c],columns_[iter->second]);
00445         }
00446         else
00447         {
00448             ret=false;
00449         }
00450     }
00451 
00452     return ret;
00453 }
00454 
00455 
00456 //========================================================================
00457 // Read a series of characters from the stream until a delimiter character or eol.
00458 //========================================================================
00459 bool mbl_table::read_delimited_string(vcl_istream& is,
00460                                       vcl_string& str,
00461                                       bool& eol,
00462                                       bool& eof)
00463 {
00464   str = "";
00465   eol = false;
00466   bool success = false;
00467 
00468   char c = 0;
00469   bool eos = false;
00470   while (!eos && is.good() && !is.eof())
00471   {
00472     is.get(c);
00473     if (c=='\n')
00474     {
00475       eol = true;
00476       eos = true;
00477     }
00478     else if (c==delimiter_)
00479     {
00480       eos = true;
00481     }
00482     else if (c==0)  // We sometimes get this at end-of-file
00483     {
00484       eof = true;
00485     }
00486     else
00487     {
00488       str += c;
00489     }
00490   }
00491 
00492   if (eos && str.length()>0)
00493   {
00494     success = true;
00495   }
00496 
00497   return success;
00498 }
00499 
00500 
00501 //========================================================================
00502 // Is another table identical to this one?
00503 //========================================================================
00504 bool mbl_table::operator==(const mbl_table& rhs) const
00505 {
00506   // Are both tables the same object - do they occupy the same memory location?
00507   if (this == &rhs)
00508   {
00509     if (verbosity_>0)
00510       vcl_cout << "Both tables are actually the same memory object!" << vcl_endl;
00511     return true;
00512   }
00513 
00514   // Is the delimiter the same?
00515   if (delimiter_ != rhs.delimiter_)
00516   {
00517     if (verbosity_>0)
00518       vcl_cout << "Tables have different delimiter characters" << vcl_endl;
00519     return false;
00520   }
00521 
00522   // Is the column headers vector the same?
00523   if (column_headers_ != rhs.column_headers_)
00524   {
00525     if (verbosity_>0)
00526       vcl_cout << "Tables have different column headers" << vcl_endl;
00527     return false;
00528   }
00529 
00530   // Is the header to index map the same?
00531   if (header_to_column_index_ != rhs.header_to_column_index_)
00532   {
00533     if (verbosity_>0)
00534       vcl_cout << "Tables have different header-to-column index map" << vcl_endl;
00535     return false;
00536   }
00537 
00538   // Are the numbers of columns the same?
00539   unsigned ncols = columns_.size();
00540   if (ncols != rhs.columns_.size())
00541   {
00542     if (verbosity_>0)
00543       vcl_cout << "Tables have different number of columns" << vcl_endl;
00544     return false;
00545   }
00546 
00547   // Is the table data the same (within the current tolerance)?
00548   bool values_different = false;
00549   for (unsigned c=0; c<ncols; ++c)
00550   {
00551     // Are the numbers of rows in this column the same?
00552     unsigned nrows = columns_[c].size();
00553     if (nrows != rhs.columns_[c].size())
00554     {
00555       if (verbosity_>0)
00556         vcl_cout << "Tables have different number of elements in some columns"
00557                  << vcl_endl;
00558       return false;
00559     }
00560 
00561     // Compare all data values in this column
00562     for (unsigned r=0; r<nrows; ++r)
00563     {
00564       double diff = columns_[c][r] - rhs.columns_[c][r];
00565       if (fractional_tolerance_)
00566       {
00567         diff /= columns_[c][r];
00568       }
00569       if (vcl_fabs(diff) > tolerance_)
00570       {
00571         if (verbosity_>0)
00572           vcl_cout << "Tables have different values in column " << c
00573                    << " (" << column_headers_[c] << "), row " << r
00574                    << ":  " << columns_[c][r] << ",  "
00575                    << rhs.columns_[c][r]
00576                    << "  (diff=" << diff << ") "
00577                    << vcl_endl;
00578 
00579         if (verbosity_<=1)
00580           return false;     // Don't bother checking any more elements
00581         else
00582           values_different = true; // Proceed to check all elements
00583       }
00584     }
00585   }
00586   if (values_different) return false;
00587 
00588   // Passed all tests, table is identical to this one.
00589   return true;
00590 }
00591 
00592 
00593 //========================================================================
00594 // Is another table different from this one?
00595 //========================================================================
00596 bool mbl_table::operator!=(const mbl_table& rhs) const
00597 {
00598   return !(*this==rhs);
00599 }
00600 
00601 
00602 //========================================================================
00603 // Set the tolerance used to determine whether table entries are equal.
00604 //========================================================================
00605 void mbl_table::set_tolerance(const double& tol,
00606                               const bool& fract/*=false*/)
00607 {
00608   tolerance_ = tol;
00609   fractional_tolerance_ = fract;
00610 }
00611 
00612 
00613 //========================================================================
00614 // Set the level of verbosity used for error output.
00615 //========================================================================
00616 void mbl_table::set_verbosity(const int& v)
00617 {
00618   verbosity_ = v;
00619 }