我想在 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];
或者您可以编写一个类来为您处理这种间接。
我会创建一个类
一个工作示例看起来像这样
#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 的类-based 索引,这将消除我的一些代码重复。还有其他方法可以将任意索引转换为基于 0 的索引,例如使用map
其他人建议的方法。但是在这种情况下,存在一些问题,例如创建者map
必须确保如果有 10 列,则 [0, 10) 中的每个索引作为映射中的值恰好出现一次。
R 中的Adata.frame
本质上只是 a list
of 列的精美包装,并且 alist
不合逻辑地与 C++ 中的std::map
1非常等价(而不是顾名思义, of std::list
)。
因此,换句话说,您可以使用与此类似的 typedef 来近似data.frame
:
typedef std::map<int, std::vector<int>> data_frame;
…但是 R 类实际上要强大得多,因为它们在某种程度上是通用的,支持数据框中的不同类型,检查所有行的长度是否相同,并允许对列和行进行命名访问。最后,我们不要忘记 R 支持处理数据帧、漂亮地打印它们以及有效地加载和保存它们。
根据您的需要,当然没有必要在 C++ 中复制所有这些,但将您的结构包装在一个类中并提供一个适当的接口来访问它绝对是有益的。
1实际上,一个std::unordered_map
. 这需要 C++11。