@@ -477,8 +477,46 @@ bool plot(const ContainerX& x, const ContainerY& y, const std::string& fmt="")
477
477
npy_intp xsize=x.size ();
478
478
npy_intp yrows=xsize, ycols=y.size ()/x.size ();
479
479
npy_intp ysize[]={yrows, ycols}; // ysize[0] must equal xsize
480
- PyObject* xarray = PyArray_SimpleNewFromData (1 , &xsize, xtype, (void *)(x.data ()));
481
- PyObject* yarray = PyArray_SimpleNewFromData (2 , ysize, ytype, (void *)(y.data ()));
480
+
481
+ // We have 2 options to pass existing data buffers to PyObject:
482
+ // PyArray_SimpleFromData() - by default creates C-style (row major) array
483
+ // PyArray_New() - uses flags, so we can specify Fotran-style (col major)
484
+ //
485
+ // In python,
486
+ // a=np.array([[3,5], [1,4], [4,1], [5,3]])
487
+ // is stored in row-major mode and has columns
488
+ // a[:,0]=[3,1,4,5] and a[:,1]=[5,4,1,3]
489
+ // Then,
490
+ // plt.plot(a)
491
+ // plots the columns of a. So in python the array is row-major, but
492
+ // plotted columnwise.
493
+ //
494
+ // For dataframes (and time series), however, it is more natural to assume
495
+ // that the data is stored contiguously in column-major mode, i.e.
496
+ // double a[] = [3, 1, 4, 5
497
+ // 5, 4, 1, 3];
498
+ // We let python know that is the case, by specifying the NPY_ARRAY_FARRAY
499
+ // flag. This rules out the usage of PyArray_SimpleFromData().
500
+ //
501
+ // Of course, in the vector-only version of this plot() function all this
502
+ // is irrelevant, because that plot is 1-dimensional anyway.
503
+ //
504
+ // If there are real-world applications that need to assume that the
505
+ // C-data is in row-major mode, we should address that.
506
+ //
507
+ // TODO:
508
+ // To that end, perhaps we can introduce C++ tags cmajor_tag and rmajor_tag
509
+ // and define a custom concept from contiguous_range, by further
510
+ // conditioning on the storage tags. The caller then specifies the major
511
+ // storage mode when wrapping the C-style array into a range.
512
+ PyObject* xarray =
513
+ PyArray_New (&PyArray_Type,
514
+ 1 , &xsize, xtype, nullptr , (void *) x.data (),
515
+ 0 , NPY_ARRAY_FARRAY, nullptr );
516
+ PyObject* yarray =
517
+ PyArray_New (&PyArray_Type,
518
+ 2 , ysize, ytype, nullptr , (void *) y.data (),
519
+ 0 , NPY_ARRAY_FARRAY, nullptr ); // column major by design!
482
520
483
521
PyObject* pystring = PyString_FromString (fmt.c_str ());
484
522
@@ -510,8 +548,16 @@ bool plot(const ContainerX& x, const ContainerY& y,
510
548
npy_intp xsize=x.size ();
511
549
npy_intp yrows=xsize, ycols=y.size ()/x.size ();
512
550
npy_intp ysize[]={yrows, ycols}; // ysize[0] must equal xsize
513
- PyObject* xarray = PyArray_SimpleNewFromData (1 , &xsize, xtype, (void *)(x.data ()));
514
- PyObject* yarray = PyArray_SimpleNewFromData (2 , ysize, ytype, (void *)(y.data ()));
551
+
552
+ // Same comments as above.
553
+ PyObject* xarray =
554
+ PyArray_New (&PyArray_Type,
555
+ 1 , &xsize, xtype, nullptr , (void *) x.data (),
556
+ 0 , NPY_ARRAY_FARRAY, nullptr );
557
+ PyObject* yarray =
558
+ PyArray_New (&PyArray_Type,
559
+ 2 , ysize, ytype, nullptr , (void *) y.data (),
560
+ 0 , NPY_ARRAY_FARRAY, nullptr ); // column major by design!
515
561
516
562
// construct positional args
517
563
PyObject* args = PyTuple_New (2 );
@@ -2995,11 +3041,11 @@ struct plot_impl<std::false_type>
2995
3041
PyObject* xarray =
2996
3042
PyArray_New (&PyArray_Type,
2997
3043
1 , &xsize, xtype, nullptr , nullptr ,
2998
- 0 , NPY_ARRAY_DEFAULT , nullptr );
3044
+ 0 , NPY_ARRAY_FARRAY , nullptr );
2999
3045
PyObject* yarray =
3000
3046
PyArray_New (&PyArray_Type,
3001
3047
2 , ysize, ytype, nullptr , nullptr ,
3002
- 0 , NPY_ARRAY_DEFAULT , nullptr );
3048
+ 0 , NPY_ARRAY_FARRAY , nullptr ); // column major!
3003
3049
PyObject* pystring = PyString_FromString (format.c_str ());
3004
3050
3005
3051
// fill the data
0 commit comments