2013-02-17 10 views
5

W poprzednim poście column vector with row means -- with std::accumulate? Zapytałem, czy to było możliwe, wykorzystując funkcjonalność STL, aby obliczyć środki rzędem macierzyobliczanie sum kolumn wektora macierzy <Wektor <double>> z iteratorami?

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

Górny odpowiedź przez @benjaminlindley jest nie tylko to, czego szukałem, to rzecz piękna. Zawsze nadzieję, że myślałem, że będzie tak łatwo obliczyć kolumna oznacza, więc równoważny STL z

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(); 

gdzie średnia nie jest wyliczana wewnątrz każdej vector<double>, ale po drugiej stronie tego samego indeksu we wszystkich wektorów:

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 

Okazuje się, że jest zupełnie inaczej - kumulacja nie chce pracować na wektorach wektorów. Czy w jakiś sposób można użyć akumulacji z operatorem []? Nie mogę nawet wymyślić pośredniej formy (aby pozbyć się pętli for i lub for j), która nie wydaje się odpowiednia.

Coś z operatorem accumulate i operatorem []? Lub bind?

+0

Jeśli uważasz, że Beniamin's anwser jest dobry (co, BTW, jest), powinieneś oznaczyć go jako zaakceptowany. – rodrigo

+0

szczerze mówiąc najtrudniejszą częścią tego będzie potencjalny short-wektor w twoim zewnętrznym wektorze wektorów. Jest to podstawowy problem podczas używania 'wektora >'. Nie ma gwarancji, że każdy wektor wewnętrzny ma ten sam rozmiar (z wyjątkiem oczywiście zakładając go w swoim własnym kodzie, który w pierwszej kolejności zapełnia tę rzecz). Nie stanowi to problemu przy wyszukiwaniu wierszy, ponieważ nie bardzo zależy ci na liczbie kolumn. – WhozCraig

+0

tak @WhozCraig mówisz, że to problem, gdy znajdujesz kolumnę-znaczy? BTW gwarancja, że ​​wewnętrzne rozmiary wektorów są takie same, jest spełnione, to nie zmienia się po inicjalizacji - "dane" to w zasadzie macierz 'wierszy' x 'kolumn' –

Odpowiedz

5

Oto coś wymyśliłem, używając for_each i 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

To jest niesamowite. Dzięki! –

+0

Używam tego rozwiązania for_each teraz w 'https: // github.com/amwink/bias/blob/master/cpp/fastecm/fastecm.cpp' –

+0

@alle_meije Cieszę się, że zadziałało dla Ciebie i dzięki za informację zwrotną. – jrok

2

pierwsze chciałbym stwierdzić, że naprawdę nie powinno gniazdo std :: wektory. Poza tym mam pewne rozwiązanie, które jest oczywiście dłuższa niż początkowego kodu, ale że może uratować w dłuższej perspektywie:

#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); 
} 

ten może być wykorzystywany w skrócie:

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"; 
} 

I zatrudniony niektóre zwiększyć funkcjonalność, aby ją skrócić, ale można to zrobić bez większego wysiłku (choć znacznie dłużej).

Ideą było stworzenie iterator nad wszystkimi kolumnami (zwanych Columns tylko dla krótkości) oraz iterator że iteracje nad wszystkimi elementami jednej kolumnie (ColumnElement również skrócona, powinna być lepiej nazwie ColumnElementIterator) i Column który reprezentuje zakres wszystkich elementów jednej kolumny.

+0

Dzięki, naprawdę docenione. Byłem ciekawy rozwiązań STL dla łatwości konserwacji i łatwości przekazywania kodu innym. Zwiększenie wydaje się jednak bardzo trudne do zignorowania. W moim przypadku - stosunkowo mała liczba (<500) stosunkowo długich rzędów (> 100 000 elementów) w gęstej matrycy, nie jestem pewien, czy wzrost prędkości czynnika 2 [http://scicomp.stackexchange.com/ pytania/3159] waży aż do dodatkowego wysiłku programistycznego. Czy wektory gniazdujące są naprawdę złym pomysłem, jeśli znasz wymiarowość i wymiary twoich danych? –

+0

Wektory zagnieżdżające mają kilka wad (rozproszone dane, możliwość niespójnych rozmiarów, trudniejszy dostęp). Zwykłym sposobem jest przechowywanie danych w 'std :: vector ' i owijanie wokół niego klasy, która mapuje dwa (lub więcej) indeksy wymiarowe na jednowymiarowy zakres indeksu wektora. Powinieneś rzucić okiem na inne implementacje macierzy. Jeśli twój kod nie jest przeznaczony do celów akademickich (powiedzmy do nauki), poleciłbym ci zapoznać się z bibliotekami, które spełniają twoje potrzeby. – Nobody

Powiązane problemy