Я хотел бы рассматривать двумерные массивы (матрицы) C ++ в C ++ так же, как я могу сделать с R-фреймами данных. Под этим я подразумеваю возможность указывать значения индексов для матриц.
Например, натуральная целочисленная матрица C ++ выглядит так:
0 1 2 3 4 ...
0 1 0 1 0 .
1 3 . . .
2 8 . .
3 . .
4 .
.
.
.
Я хотел бы указать индексы в матрице, чтобы они были такими, например:
5 7 8 13 24 ...
0 1 0 1 0 .
1 3 . . .
2 8 . .
6 . .
8 .
.
.
.
Любой совет будет высоко ценится.
Если вы хотите переключать столбцы, строки матриц, вы можете использовать некоторую косвенность:
indexTable[0][0] = 0; // map row index 0 to 0
indexTable[1][0] = 5; // map column index 0 to 5
и используйте это так:
value = matrix[indexTable[0][RowIndex]][indexTable[1][ColumnIndex];
или вы можете написать класс для обработки этого косвенного для вас.
data.frame
в R, по сути, просто модная обертка для list
столбцов и list
это — нелогично — близкий эквивалент std::map
1 в C ++ (а не, как следует из названия, std::list
).
Другими словами, вы можете просто использовать typedef, подобный этому, чтобы приблизить data.frame
:
typedef std::map<int, std::vector<int>> data_frame;
… Но классы R на самом деле немного более мощные, поскольку они в некоторой степени универсальны, поддерживают разные типы внутри фрейма данных, проверяют, что все строки имеют одинаковую длину, и разрешают именованный доступ для столбцов и строк. И, наконец, давайте не будем забывать, что R поддерживает работу с фреймами данных, их красивую печать, эффективную загрузку и сохранение.
В зависимости от того, что вы нужно, конечно, нет необходимости копировать все это в C ++, но определенно полезно обернуть вашу структуру внутри класса и предоставить надлежащий интерфейс для доступа к ней.
1 На самом деле, std::unordered_map
, Это требует C ++ 11.
Я хотел бы создать класс, который
Рабочий пример будет выглядеть примерно так
#include <iostream>
#include <vector>
#include <algorithm>
#include <stdexcept>
#include <iterator>
#include <cassert>
using namespace std;
class DataFrame
{
vector<int> data;
public:
typedef vector<ssize_t> idx_t;
private:
idx_t rowIdx;
idx_t colIdx;
public:
DataFrame(const idx_t &rowIdx, const idx_t &colIdx)
: data(rowIdx.size() * colIdx.size())
, rowIdx(rowIdx)
, colIdx(colIdx)
{
assert(is_sorted(rowIdx.begin(), rowIdx.end()));
assert(is_sorted(colIdx.begin(), colIdx.end()));
}
int& operator()(int i, int j)
{
idx_t::iterator itI, itJ;
itI = lower_bound(rowIdx.begin(), rowIdx.end(), i);
if(rowIdx.end() == itI || i != *itI) throw out_of_range("could not find specified row");
itJ = lower_bound(colIdx.begin(), colIdx.end(), j);
if(colIdx.end() == itJ || j != *itJ) throw out_of_range("could not find specified col");
return data[distance(rowIdx.begin(), itI)*colIdx.size() +
distance(colIdx.begin(), itJ)];
}
vector<int> & getData() { return data; }
};int main()
{
DataFrame::idx_t rI, cI;
rI.push_back(3);
rI.push_back(5);
cI.push_back(2);
cI.push_back(3);
cI.push_back(10);
DataFrame df(rI, cI);
df(3,2) = 1;
df(3,3) = 2;
df(3,10) = 3;
df(5,2) = 4;
df(5,3) = 5;
df(5,10) = 6;
ostream_iterator<int> out_it(cout, ", ");
copy(df.getData().begin(), df.getData().end(), out_it);
cout << endl;
return 0;
}
Произвольные индексы каждой строки / столбца указываются в векторе. Для поддержания некоторой производительности код требует, чтобы индексы монотонно увеличивались. (Если у вас есть C ++ 11, это проверяется в ctor; если у вас нет C ++ 11, то у вас нет is_sorted
функция. Кроме того, этот код не проверяет уникальность произвольных индексов.)
Когда вы получаете доступ к данным, он просто выполняет двоичный поиск по каждому вектору индексов, чтобы найти позицию в векторе, которая соответствует произвольному индексу, и использует эту позицию в качестве соответствующего индекса для базовых данных. Существует простое преобразование из 2D-индексов в 1D-индекс.
Вы можете проверить, что моя проверка ошибок индекса верна для всех комбинаций плохих / хороших индексов, если вам нужно беспокоиться об этом.
Я оставлю это вам, чтобы добавить больше надежности / функциональности с точки зрения const
методы доступа, различные конструкторы и т. д. Если вы хотите обобщить это для массивов измерения, отличных от 2, я рекомендую вам создать класс для простого преобразования произвольного индекса в индекс на основе 0, и это избавит от некоторых повторений кода. иметь. Существуют также другие способы преобразования произвольного индекса в индекс на основе 0, например, с помощью map
как другие предложили. В этом случае, однако, есть некоторые проблемы, такие как создатель map
Было бы необходимо убедиться, что если есть, скажем, 10 столбцов, каждый индекс в [0, 10) отображается ровно один раз в качестве значения на карте.