2013-02-17 7 views
5

В предыдущем посте column vector with row means -- with std::accumulate? я спросил, если это было возможно, используя функциональные возможности STL, для вычисления строк средства матрицывычислительная колонка суммы матричного вектора <вектор <double>> с итераторами?

vector< vector<double> > data (rows, vector<double> (columns)); 

Верхний ответ на @benjaminlindley не только то, что я искал, это вещь красоты. Вечно надеюсь, я думал, что это будет так легко вычислить столбец означает, так что STL эквивалент

vector<double> colmeans(data[0].size()); 
    for (int i=0; i<data.size(); i++) 
     for (int j=0; j<data[i].size(); j++)    
      colmeans[j] += data[i][j]/data.size(); 

, где среднее значение не вычисленной внутри каждого vector<double>, но по тем же показателем всех векторов:

colmeans[0]  == (data[0][0] + data[1][0] + ... data[rows][0])/rows 
colmeans[1]  == (data[0][1] + data[1][1] + ... data[rows][1])/rows 
colmeans[2]  == (data[0][2] + data[1][2] + ... data[rows][2])/rows 
... 
colmeans[columns] == (data[0] [columns] + 
         data[1] [columns] + 
         ... 
         data[rows][columns])/rows 

Оказывается, совсем другое - накапливаться не хочет работать над векторами векторов. Возможно ли использование накопленного с помощью оператора []? Я даже не могу придумать промежуточную форму (чтобы избавиться от цикла for i или for j), который не кажется правильным.

Что-то с accumulate и оператором []? Или bind?

+0

Если вы считаете, что Анжела Вениамина хорошая (что, кстати, это так), вы должны отметить ее как принятую. – rodrigo

+0

Честно говоря, самая сложная часть этого будет потенциальным коротким вектором в ваших внешних векторах векторов. Это основная проблема при использовании 'vector >'. Нет никакой гарантии, что каждый внутренний вектор имеет одинаковый размер (за исключением, конечно, допущения его в вашем собственном коде, который заполняет эту вещь в первую очередь). Это не проблема при поиске строк, поскольку вам не важно, сколько столбцов есть. – WhozCraig

+0

так @WhozCraig, вы говорите, что это проблема при поиске столбцов? BTW гарантируется, что размеры внутреннего вектора одинаковы, это не меняется после инициализации. 'Data' - это в основном матрица' rows' x 'columns' –

ответ

5

Вот то, что я придумал, используя for_each и transform:

std::vector<std::vector<double>> data { {1,2,3}, {1,2,3}, {1,2,3} }; 

std::vector<double> colsums(data[0].size()); // initialize the size 
               // to number of columns 

std::for_each(data.begin(), data.end(), 

    [&](const std::vector<double>& row) 
    { 
     // Use transform overload that takes two input ranges. 
     // Note that colsums is the second input range as well as the output range. 
     // We take each element of the row and add it to the corresponding 
     // element of colsums vector: 
     std::transform(row.begin(), row.end(), colsums.begin(), colsums.begin(), 
         [](double d1, double d2) { return d1 + d2; }); 
    }); 

std::cout << "Column means: "; 
std::transform(
    colsums.begin(), colsums.end(), 
    std::ostream_iterator<double>(std::cout, " "), 
    [&data](double d) { return d/data.size(); }); 

LWS Demo

+0

Это потрясающе. Благодаря! –

+0

Я использую это решение for_each сейчас в 'https: // github.com/amwink/bias/blob/master/cpp/fastecm/fastecm.cpp' –

+0

@alle_meije Рад, что это сработало для вас и благодарит за отзыв. – jrok

2

Прежде всего позвольте мне заявить, что вы действительно не должны гнездо StD :: векторы. Кроме того, я получил какое-то решение, которое, конечно, больше, чем ваш исходный код, но это может спасти в долгосрочной перспективе:

#include <vector> 
#include <boost/iterator/iterator_adaptor.hpp> 
#include <boost/iterator/counting_iterator.hpp> 

typedef std::vector<std::vector<double> > Data; 

struct ColumnElement : boost::iterator_adaptor<ColumnElement, 
               Data::const_iterator, 
               const double> { 
     int col; 

     ColumnElement(int col, const Data::const_iterator &iter) 
     : iterator_adaptor(iter), col(col) 
     {} 
     const double& dereference()const { return (*base())[col]; } 
}; 

struct Column { 
     int col; 
     const Data *data; 

     Column(int col, const Data *data) : col(col), data(data) {} 
     ColumnElement begin()const { return ColumnElement(col, data->begin()); } 
     ColumnElement end()const { return ColumnElement(col, data->end()); } 
     int size()const { return std::distance(begin(), end()); } 
}; 

struct Columns : boost::iterator_adaptor<Columns, boost::counting_iterator<int>, 
             Column, boost::use_default, Column> { 
     const Data *data; 

     Columns(int col, const Data *data): iterator_adaptor(col), data(data) {} 

     Column dereference()const { return Column(*base(), data); } 
}; 

Columns columnsBegin(const Data &data) { return Columns(0, &data); } 
Columns columnsEnd(const Data &data) { 
     return Columns(data.empty() ? 0 : data.front().size(), &data); 
} 

Это может быть использовано в короткий:

double Mean(const Column &d) { 
     return std::accumulate(d.begin(), d.end(), 0.0)/d.size(); 
} 

int main() { 
     Data data = { {1, 2, 3}, 
         {2, 2, 2}, 
         {9, 8, 7}}; 
     std::vector<double> colMeans(data[0].size()); 
     std::transform(columnsBegin(data), columnsEnd(data), 
         colMeans.begin(), Mean); 
     std::copy(colMeans.begin(), colMeans.end(), 
        std::ostream_iterator<double>(std::cout, ",")); 
     std::cout << "\n"; 
} 

я использовал некоторые повысить функциональность, чтобы сократить его, но это можно было бы сделать без повышения (хотя бы намного дольше).

Идея заключалась в том, чтобы создать итератор всех столбцов (называемого Columns только для краткости) и итератор, который перебирает все элементы одного столбца (ColumnElement, а также укороченные, должно быть лучше по имени ColumnElementIterator) и Column, который представляет диапазон всех элементов одной колонки.

+0

Спасибо, очень признателен. Мне было интересно узнать о решениях STL для удобства обслуживания и простоты передачи кода другим. Однако Boost очень сложно игнорировать. В моем случае - относительно небольшое число (<500) относительно длинных строк (> 100 000 элементов) в плотной матрице, я не уверен, что коэффициент усиления коэффициента 2 [http://scicomp.stackexchange.com/ вопросы/3159] весит до дополнительных усилий по программированию. Является ли вложенные векторы действительно такой плохой идеей, если вы знаете размерность + размеры ваших данных? –

+0

Вложенные векторы имеют несколько недостатков (разбросанные данные, возможность несогласованных размеров, более жесткий доступ). Обычный способ для этого - хранить данные в 'std :: vector ' и обертывать вокруг него класс, который отображает два (или более) размерных индекса на одномерный индексный диапазон вектора. Вы должны взглянуть на другие реализации матриц. Если ваш код не предназначен для академических целей (скажем, обучения), я бы порекомендовал вам взглянуть на некоторые библиотеки, которые отвечают вашим потребностям. – Nobody