How can I create an array of vector or vectors ?

Suppose I have two vector of vectors: vector< vector<double> > v1, v2; created inside a function, I want to return these two vectors as something like this:

1
2
3
4
5
 vector< vector<double> > vec_arr[2]
       vec_arr[0] = v1, vec_arr[1] = v2;

 return vec_arr;
 


and then in the main program I want to use the elements of this array as vector of vectors.

Can this be done ? Or at least something as simple as this ? Because I am really new to C++ and need something simple. But more sophisticated coding is of course welcome provided you explain in a simple way.
nth-D vectors must be initialized.

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
#include <iostream>
#include <vector>
using namespace std;

int main() {
  
  //init vectors 
 //vector<vector<type>> nameOfVar(row,vector<type>(colum));
  vector<vector<int>> myVectorOfVectors(1, vector<int>(1));

  //add a value
  myVectorOfVectors[0][0] = 5;

  //resize 
  //localVector.resize(row vector<type>(colum, init value));
  myVectorOfVectors.resize(2, vector<int>(2, 0));

  //add more values
  myVectorOfVectors[0][1] = 6;
  myVectorOfVectors[1][0] = 7;
  myVectorOfVectors[1][1] = 8;

  // print 
  cout << myVectorOfVectors[0][0] << endl;
  cout << myVectorOfVectors[0][1] << endl;
  cout << myVectorOfVectors[1][0] << endl;
  cout << myVectorOfVectors[1][1] << endl;

  return 0;
}

Last edited on
closed account (48T7M4Gy)
@OP You are creating an array of vectors of vectors which is also commonly described as an array of matrices. Good practice in C++ and usage of STL containers indicates instead of a c-style array, if that is what is intended, make use of vectors throughout and you have a vector of vector of vectors. Try it out, it works.

... and makes sense
Last edited on
@Bdanielz (483), @kemort (4140) thanks a lot, Bdanielz's answer was of great help. I seem to have solved my problem.

The problem: I needed to return 4 vectors x, y, eror_x, error_y from a function, so I did:

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

vector < vector<double> > func(type some_data)
{
       vector< vector<double> > stats;
       const int n = some_number;
 
       stats.resize(4);  // I needed 4 vectors
       stats[0].resize(n); // each of size n
       stats[0].resize(n);
       stats[0].resize(n);
       stats[0].resize(n);


              for(int i = 0; i < n; ++i)
              {
                       stats[0][i] = something0;
                       stats[1][i] = something1; 
                       stats[2][i] = something2;
                       stats[3][i] = something1;
              }


       return stats;
}



This function func() returns me an array of four vectors and I can use them in my main.
closed account (48T7M4Gy)
@nathatanu0

Without completely understanding what your
vector etc func(a,b,c,d) is supposed to be doing and the nature of the parameters you are passing to it, I suggest you might not need vectors at all.

Sure, the function can only return a single value and that can be a vector or whatever, but if all you are doing is using the function to modify the original parameter values all you need do is pass-by-reference instead of pass_by_value and the function can be
void func(a,b,c,d) where a,b,c,d are references to the called objects whatever type they are. There is no explicit return statement at all.

http://www.cplusplus.com/doc/tutorial/functions/
@kemort (4141) in the end I need vectors. I have a data of (x,y) type, I need to divide them in bins and average them over those bins and collect those averages and corresponding standard errors. In the end I will have to use something called a TGraph a ROOT object which accepts vectors as it's input parameters. There are 5 input parameters, size of the vector (in our case number of bins), the x-data, y-data, x-error and y-error, and they all must be c++ vectors. I could create those vectors separately but I needed to create a function and save it in a header that always return me those four vectors that I will need often.
closed account (48T7M4Gy)
@nathatnuO
I won't interfere any more but I had a good look at ROOT and TGraph along with a couple of their samples and it is pretty good/straightforward, not unlike other similar stuff around.

To be honest I think you are making it difficult for yourself. I'd just feed the TGraph class and initialise a TGraph object with the 5 pieces of data as they demonstrate. How you currently want to generate the attributes seems to me to be too complicated and prone to error.

But good luck with it. Cheers :)
@kemort (4144) I have just started to learn all these stuff ... my current strategy is to somehow make things work first and then try to clean it up :) it's the second step where I need more guidance.. please feel free to interfere :) anytime ...
closed account (48T7M4Gy)
@nathatanu0
Have you seen/tried their Users Guide?
@kemort (4146) honestly ? Not that much really ! I started then thought maybe looking at some existing codes that work will be more effective, I could back engineer and get things done and in fact I learnt a lot of things and I now am in a position to start learning more formally.. so yeah I think it's time I go through that guide. Thanks for the suggestion. I will post another question that will involve more C++ but I will be asking about how to include ROOT libraries in my C++ and use g++ to compile my codes and avoiding "ROOT's shell" completely. If I am not wrong, by now you have a good understanding of at least what ROOT is and I believe you are an expert in C++ maybe you can help. I will be posting the topic in a more formal and complete way so that many others can get benefited from the answers.
closed account (48T7M4Gy)
I'm a bit more reserved about my C++ expertise than you but, that aside, I've had a chance tp play around with ROOT and I think I get the idea. They give you so many samples, that's where I'd start. With this one I can see where you are coming from and how it works. It's so simple.

The way I understand it, the bins, their weird name, are just the data arrays x[n], y[n], ex[n] and ey[n] and n used to initialise the new TGraphErrors object gr, along with all gr's other properties. gr sits on the canvas and gets drawn there. This is all very straightforward.

So, your problem seems to be just generating those parameters so the gr object can be created.

Also, the way I understand ROOT it has its own inbulit c++ compiler interpreter called cling. You can also use XCode. I haven't tried either yet.

As I said, I'd stick to the User Guide because so far I've found it fairly easy to get it doing some simple plots. Next step is to use one of their macros, after that to read in a data file. Vectors for me are a very low priority.

PS I think GNUPlot is better but CERN probably think otherwise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ROOTSYS/tutorials/graphs/gerrors.C.

{
   c1 = new TCanvas("c1","A Simple Graph with error bars",200,10,700,500);
   c1->SetGrid();

   // create the coordinate arrays
   Int_t n = 10;
   Float_t x[n]  = {-.22,.05,.25,.35,.5,.61,.7,.85,.89,.95};
   Float_t y[n]  = {1,2.9,5.6,7.4,9,9.6,8.7,6.3,4.5,1};

   // create the error arrays
   Float_t ex[n] = {.05,.1,.07,.07,.04,.05,.06,.07,.08,.05};
   Float_t ey[n] = {.8,.7,.6,.5,.4,.4,.5,.6,.7,.8};

   // create the TGraphErrors and draw it
   gr = new TGraphErrors(n,x,y,ex,ey); // TOO EASY
   gr->SetTitle("TGraphErrors Example");
   gr->SetMarkerColor(4);
   gr->SetMarkerStyle(21);
   gr->Draw("ALP");
   c1->Update();
}
Last edited on
@kemort (4147) this is great. But I see that you are using arrays instead of vectors, isn't it the same thing in principle (forgetting the dynamic nature) ? What I am doing right now is,

1. Pulling X and Y data from a root file (that can be any other file).

2. There are thousands of Y - points within a tiny range of X and hence a binning is necessary to see the average behavior (trend or profile). And so I divide the whole X - range in a small "nbin number of bins" of the order of 10 say.

3. I take those (X,Y) data (basically 2 arrays of huge size, millions of points ), average over each bins and get 4 arrays (X_averages, Y_averages, X_errors, Y_errors) of much smaller size "nbin" each.

4. I can feed the TGraphErrors with these data of course as you described above.


But as I will be doing this a lot over thousands of ROOT files and in different programs and situations and over different kinds of X and Y (mass, charge), (position, momentum) etc etc I thought it would be wise to write a function in a header file that takes (X, Y, nbin) and returns a vector of vectors or lets say arrays of arrays:



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
// the averaging function
// <vector> is included

vector< vector <double> > get_avr_error(vector<double> x, vector<double> y, int nbin)
{

    vector< vector<double> > avr_error;
    avr_error.resize(4);

    double x_min = *min_element(x.begin(), x.end());
    double x_max = *max_element(x.begin(), x.end());
    double bin_width = (double)(x_max-x_min)/nbin;

    avr_error[0].resize(nbin);   // will contain my X - data
    avr_error[1].resize(nbin);   // will contain my Y - data
    avr_error[2].resize(nbin);    // will contain my X - error data
    avr_error[3].resize(nbin);     // will contain my Y - error data

    for (int i = 0; i < nbin; ++i)
    {
        avr_error[0][i] = x_min + i*bin_width + bin_width/2.;
        double sum = 0.;
        double sumSqr = 0.;
        avr_error[2][i] = 0.;    // there's no error in X in my case

        int counts = 0;

        double x_val = x[i];
        while (x_val < x_min + (i+1)*bin_width)
        {

            sum += y[i+counts];                          // contributes to mean
            sumSqr += pow(y[i+counts], 2);  // contributes to standard deviation
            counts++;
            x_val = x[i+counts];
        }


        if(counts == 0)           // to avoid empty bin problem               
        {
            avr_error[0][i] = 0;
            avr_error[1][i] = 0;
            avr_error[3][i] = 0;

        }
        else
        {
            avr_error[1][i] = sum/counts;  // mean
            avr_error[3][i] = sqrt(((sumSqr/counts)-pow(avr_error[1][i],2))/counts); // standard error
        }

    }




return avr_error;

}


This function returns me a vector of size 4 whose elements are vectors of size "nbins" each. But your code helped me understand that TGraphs can take arrays as well but in any case I want to use a function to return me those four variables.
Last edited on
@kemort (4147) as I said, things were foggy a few weeks ago when I was first introduced to all these things.. ROOT/C++ !! Now it has started to become (a bit) clearer and I would surely take your advice seriously on following the ROOT guide from now on.
closed account (48T7M4Gy)
@nathatanu0
I get the idea with your multiple files and vectors and stuff but I would take a slightly different view, certainly at my stage in using ROOT.

Forget how you get the data for a while, arrays or vectors doesn't matter, and consider how you get multiple graphs on a canvas. This is shown in section 4.9 with Multigraphs. You create a canvas, then 2 graphs (of different types) based on separate data arrays. These 2 graphs are added to a Multigraph object and the Multigraph object is drawn.

Note that the two graphs are supplied with two completely separate data arrays. So once you get the sample to work I would start looking at how I can read a root file and feed those arrays. I haven't checked it thoroughly out but I suspect all you have to do is read the input/output section and all you need is the file name.

So, my point is, once that works, and ROOT claims that's what it is for - large amounts of data/files - if you have a 1000 files and therefore lots of bins associated with each one then it is the file names and associated graphs that are arrayed (indexed, vectored) not the lower level data. The graphs encapsulate the data 'automatically' so you don't need the extra dimensions.

It will be easier to implement than explain so I might have a go. Meanwhile this is their sample.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
   // create the points
   Int_t n = 10;
   Double_t x[n]  = {-.22,.05,.25,.35,.5,.61,.7,.85,.89,.95};
   Double_t y[n]  = {1,2.9,5.6,7.4,9,9.6,8.7,6.3,4.5,1};
   Double_t x2[n]  = {-.12,.15,.35,.45,.6,.71,.8,.95,.99,1.05};
   Double_t y2[n]  = {1,2.9,5.6,7.4,9,9.6,8.7,6.3,4.5,1};

   // create the width of errors in x and y direction
   Double_t ex[n] = {.05,.1,.07,.07,.04,.05,.06,.07,.08,.05};
   Double_t ey[n] = {.8,.7,.6,.5,.4,.4,.5,.6,.7,.8};

   // create two graphs
   TGraph *gr1 = new TGraph(n,x2,y2);
   TGraphErrors *gr2 = new TGraphErrors(n,x,y,ex,ey);

   // create a multigraph and draw it
   TMultiGraph  *mg  = new TMultiGraph();
   mg->Add(gr1);
   mg->Add(gr2);
   mg->Draw("ALP");
}
Last edited on
I know TMultiGraph, it draws various graphs on the same canvas, so far I have been able to create arrays of TGraphErrors and feed them to different TPad(s) on TCanvas(es) by dividing them etc and also draw them on a single canvas by overlapping them using TMultiGraph.
closed account (48T7M4Gy)
@natatanu0
This might be what you are driving at? If the data is taken off the file then there are numerous considerations to be made on error trapping, differentiating what type of object is being described and how well-formed the data is, particularly what form it comes in which is not clear to me - I assume it is not ROOT standard serialized form in which case you wouldn't do any of this because the data would be handled by direct ROOT input/output functionality.

There is scope for post processing the data as it is read in by the function or later via the vectors in main. Sums, averages, histogram bins (LOL) all the other stuff falls into place as additional lines of code within the file-read function or in main.

In the case of multiple files that would require additional lines to include the file names of the source data. Another vector in main() could hold those or you could take advantage of ROOT's built in directory capabilities maybe.

In the case of multiple vectors per individual file, I'll leave that for 'another day'. It's simply another dimension. No big deal AFAICS.

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
#include <iostream>
#include <vector>
#include <iomanip>
#include <fstream>
#include <string>

void vector_from_ARRAY(std::vector <float> *);
void vector_from_FILE(std::string, std::vector<float>*, int);

void displayVector(std::vector <float> *);

int main()
{
    const int limit = 5;
    
    // CASE 1 - FROM ARRAY//
    std::vector <float> *data_A = new std::vector <float>[limit];
    vector_from_ARRAY(data_A);
    
    // TEST TO SEE WHETHER DATA IS ACCEPTED
    std::cout << "*** GET VECTORS FROM AN ARRAY ***\n";
    for( int i = 0; i < limit; i++)
    {
        std::cout << "Vector[" << i << "]: ";
        displayVector(&data_A[i]);
    }
    
    //CLEANUP
    delete [] data_A;
    
    
    
    
    
    // CASE 2 - FROM FILE //
    std::vector <float> *data_B = new std::vector <float>[limit];
    vector_from_FILE( "CERN_ROOT.dat", data_B, limit );
    
    // TEST TO SEE DATA IS ACCEPTED
    std::cout << "\n*** GET VECTORS FROM A FILE ***\n";
    for( int i = 0; i < limit; i++)
    {
        std::cout << "Vector[" << i << "]: ";
        displayVector(&data_B[i]);
    }
    
    //CLEANUP
    delete [] data_B;
    
    return 0;
}

void vector_from_ARRAY(std::vector<float>* aVector)
{
    // SAMPLE OF DATA FROM SOME SOURCE AND PREPARED AS REQUIRED
    const int n = 10;
    float x[n]  = { -.22, .05, .25, .35, .5, .61, .7, .85, .89, .95 };
    float y[n]  = { 1, 2.9, 5.6, 7.4, 9, 9.6, 8.7, 6.3, 4.5, 1 };
    
    // create the error arrays
    float ex[n] = {.05, .1, .07, .07, .04, .05, .06, .07, .08, .05 };
    float ey[n] = {.8,.7,.6,.5,.4,.4,.5,.6,.7,.8};
    
    aVector[0].push_back(n);
    
    aVector[1].assign( x, x + (int)aVector[0][0] );
    aVector[2].assign( y, y + (int)aVector[0][0] );
    aVector[3].assign( ex, ex + (int)aVector[0][0] );
    aVector[4].assign( ey, ey + (int)aVector[0][0] );
    
    return;
}

void vector_from_FILE(std::string aFile_name, std::vector<float>* aVector, int aLimit)
{
    int no_per_vector = 0;
    float temp;
    
    std::ifstream dataFile(aFile_name);
    
    if (dataFile.is_open())
    {
        dataFile >> no_per_vector;
        aVector[0].push_back(no_per_vector);
        
        for(int i = 1; i < aLimit; i++)
        {
            for( int j = 0; j < no_per_vector; j++)
            {
                dataFile >> temp;
                aVector[i].push_back(temp);
            }
        }
        dataFile.close();
    }
    
    else std::cout << "Unable to open file\n";
    
    return;
}

void displayVector(std::vector<float>* aVector)
{
    for( auto it = aVector->begin(); it != aVector -> end(); it++)
        std::cout << std::setw(6) << *it ;
    std::cout << '\n';
    
    return;
}

CERN_ROOT.dat file
10
-0.22 0.05 0.25 0.35 0.5 0.61 0.7 0.85 0.89 0.95
1 2.9 5.6 7.4 9 9.6 8.7 6.3 4.5 1
0.05 0.1 0.07 0.07 0.04 0.05 0.06 0.07 0.08 0.05
0.8 0.7 0.6 0.5 0.4 0.4 0.5 0.6 0.7 0.8


Program output
*** GET VECTORS FROM AN ARRAY ***
Vector[0]:     10
Vector[1]:  -0.22  0.05  0.25  0.35   0.5  0.61   0.7  0.85  0.89  0.95
Vector[2]:      1   2.9   5.6   7.4     9   9.6   8.7   6.3   4.5     1
Vector[3]:   0.05   0.1  0.07  0.07  0.04  0.05  0.06  0.07  0.08  0.05
Vector[4]:    0.8   0.7   0.6   0.5   0.4   0.4   0.5   0.6   0.7   0.8

*** GET VECTORS FROM A FILE ***
Vector[0]:     10
Vector[1]:  -0.22  0.05  0.25  0.35   0.5  0.61   0.7  0.85  0.89  0.95
Vector[2]:      1   2.9   5.6   7.4     9   9.6   8.7   6.3   4.5     1
Vector[3]:   0.05   0.1  0.07  0.07  0.04  0.05  0.06  0.07  0.08  0.05
Vector[4]:    0.8   0.7   0.6   0.5   0.4   0.4   0.5   0.6   0.7   0.8
Program ended with exit code: 0


I hope it's of use,
Cheers, Happy New Year.
Last edited on
@kemort (4151) I am more than sure that your last code will prove to be extremely useful to me but right now it's a lot to take in :) at least a few hours for sure :) tomorrow I am gonna go through this ... thanks a lot and wish you a Happy New Year.
Topic archived. No new replies allowed.