-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathopensx.hpp
242 lines (221 loc) · 9.3 KB
/
opensx.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/**
* This file is a part of OpenCX, especially about functionalities not depending on OpenCV.
* - Homepage: https://github.com/sunglok/opencx
*/
/**
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <[email protected]> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Sunglok Choi
* ----------------------------------------------------------------------------
*/
#ifndef __OPEN_SX__
#define __OPEN_SX__
#include <string>
#include <functional>
#include <algorithm>
#include <vector>
#include <fstream>
#include <sstream>
namespace cx
{
/**
* Remove space at the left of the given string
* @param text The given string
* @return The trimmed string
*/
inline std::string trimLeft(std::string text)
{
// Reference: https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
text.erase(text.begin(), std::find_if(text.begin(), text.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
return text;
}
/**
* Remove space at the right of the given string
* @param text The given string
* @return The trimmed string
*/
inline std::string trimRight(std::string text)
{
text.erase(std::find_if(text.rbegin(), text.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), text.end());
return text;
}
/**
* Remove space at the left and right of the given string
* @param text The given string
* @return The trimmed string
*/
inline std::string trimBoth(std::string text) { return trimLeft(trimRight(text)); }
/**
* Make the given string to its lower cases
* @param text The given string
* @return The transformed string with lower cases
*/
inline std::string toLowerCase(std::string text)
{
// Reference: https://stackoverflow.com/questions/313970/how-to-convert-stdstring-to-lower-case
std::transform(text.begin(), text.end(), text.begin(), [](unsigned char c) { return std::tolower(c); });
return text;
}
/**
* @brief A CSV File Reader
*
* A CSV file is a popular text file format for reading and writing tubular data.
* It is also supported by many other applications such as Microsoft Excel and Open Office.
* This is an extension of a 2D vector of string with additional functions for reading a CSV file and extracting its columns as a 2D vector of doubles (a.k.a. float64) or integers (a.k.a. int32) or strings.
*/
class CSVReader : public std::vector<std::vector<std::string>>
{
public:
/** A type for 2D vector of strings */
typedef CSVReader String2D;
/** A type for 2D vector of doubles */
typedef std::vector<std::vector<double>> Double2D;
/** A type for 2D vector of integers */
typedef std::vector<std::vector<int>> Int2D;
/**
* Open a CSV file
* @param csv_file The filename to read
* @param separator A character which separate each column
* @return True if successful (false if failed)
*/
bool open(const std::string& csv_file, char separator = ',')
{
std::ifstream file(csv_file);
if (!file.is_open()) return false;
this->clear();
std::string line;
while (getline(file, line))
{
std::vector<std::string> datum;
std::string element;
std::stringstream temp(line);
while (getline(temp, element, separator))
datum.push_back(trimBoth(element));
this->push_back(datum);
}
return true;
}
/**
* Extract the desired columns as a 2D vector of strings
* @param row_start A starting row to skip headers
* @param columns The desired columns to extract
* @param invalid_val The value to represent invalid (e.g. non-numeric) elements
* @return The selected columns as a 2D vector of strings
*/
String2D extString2D(int row_start = 0, const std::vector<size_t> columns = std::vector<size_t>(), const std::string& invalid_val = "None")
{
String2D data;
if (!this->empty())
{
// Select all columns if the given is empty
std::vector<size_t> col_select = columns;
if (col_select.empty()) col_select = getColumns(this->front().size());
// Extract the selected columns
for (size_t row = row_start; row < this->size(); row++)
{
const std::vector<std::string>& row_data = this->at(row);
if (row_data.empty()) continue;
std::vector<std::string> vals;
for (auto col = col_select.begin(); col != col_select.end(); col++)
{
if (*col < row_data.size()) vals.push_back(row_data[*col]);
else vals.push_back(invalid_val);
}
data.push_back(vals);
}
}
return data;
}
/**
* Extract the desired columns as a 2D vector of doubles (a.k.a. float64)
* @param row_start A starting row to skip headers
* @param columns The desired columns to extract
* @param invalid_val The value to represent invalid (e.g. non-numeric) elements
* @return The selected columns as a 2D vector of doubles
*/
Double2D extDouble2D(int row_start = 0, const std::vector<size_t> columns = std::vector<size_t>(), double invalid_val = std::numeric_limits<double>::quiet_NaN())
{
Double2D data;
if (!this->empty())
{
// Select all columns if the given is empty
std::vector<size_t> col_select = columns;
if (col_select.empty()) col_select = getColumns(this->front().size());
// Extract the selected columns
for (size_t row = row_start; row < this->size(); row++)
{
const std::vector<std::string>& row_data = this->at(row);
if (row_data.empty()) continue;
std::vector<double> vals;
for (auto col = col_select.begin(); col != col_select.end(); col++)
{
double val = invalid_val;
try
{
if (*col < row_data.size())
val = std::stod(row_data[*col]);
}
catch (std::exception e) { }
vals.push_back(val);
}
data.push_back(vals);
}
}
return data;
}
/**
* Extract the desired columns as a 2D vector of integers (a.k.a. int32)
* @param row_start A starting row to skip headers
* @param columns The desired columns to extract
* @param invalid_val The value to represent invalid (e.g. non-numeric) elements
* @return The selected columns as a 2D vector of integers
*/
Int2D extInt2D(int row_start = 0, const std::vector<size_t> columns = std::vector<size_t>(), int invalid_val = -1)
{
Int2D data;
if (!this->empty())
{
// Select all columns if the given is empty
std::vector<size_t> col_select = columns;
if (col_select.empty()) col_select = getColumns(this->front().size());
// Extract the selected columns
for (size_t row = row_start; row < this->size(); row++)
{
const std::vector<std::string>& row_data = this->at(row);
if (row_data.empty()) continue;
std::vector<int> vals;
for (auto col = col_select.begin(); col != col_select.end(); col++)
{
int val = invalid_val;
try
{
if (*col < row_data.size())
val = std::stoi(row_data[*col]);
}
catch (std::exception e) { }
vals.push_back(val);
}
data.push_back(vals);
}
}
return data;
}
/**
* Get a permutation of the given length, starting value, and step
* @param size The length of the permutation
* @param start The given starting value
* @param step The given step
* @return A series of integers
*/
static std::vector<size_t> getColumns(size_t size, size_t start = 0, size_t step = 1)
{
std::vector<size_t> permutation(size);
for (size_t i = 0; i < permutation.size(); i++)
permutation[i] = start + step * i;
return permutation;
}
}; // End of 'CSVReader'
} // End of 'cx'
#endif // End of '__OPEN_SX__'