Skip to content

Commit b0f57e5

Browse files
committed
specify column-major mode flag (NPY_ARRAY_FPARRAY) in the contiguous range functions, for multicolumn plot
1 parent 3f5b276 commit b0f57e5

File tree

2 files changed

+73
-16
lines changed

2 files changed

+73
-16
lines changed

examples/ranges.cpp

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,21 @@ int main()
2121
{
2222
plt::detail::_interpreter::get();
2323

24+
// C-style arrays with multiple rows.
2425

2526
#if __cplusplus >= CPP20
2627

27-
// C-style arrays with multiple rows.
28+
time_t t[]={1, 2, 3, 4};
2829

2930
// Care with column-major vs row-major!
3031
// C and Python are row-major, but usually a time series is column-major
3132
// and we want to plot the columns.
3233
// In the example below, these columns are [3,1,4,5] and [5,4,1,3], so
3334
// the data must be stored like this:
34-
time_t t[]={1, 2, 3, 4};
3535
double data [] = {
36-
3, 5,
37-
1, 4,
38-
4, 1,
39-
5, 3
40-
};
36+
3, 1, 4, 5,
37+
5, 4, 1, 3
38+
}; // contiguous data, column major!
4139

4240
// Use std::span() to convert to a contiguous range (O(1)).
4341
// Data won't be copied, but passed as a pointer to Python.
@@ -49,7 +47,7 @@ int main()
4947
#else
5048

5149
cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << ": "
52-
<< "No support for contiguous ranges." << endl;
50+
<< "No support for C-style arrays in C++ <= 17" << endl;
5351

5452
#endif
5553

@@ -89,16 +87,29 @@ int main()
8987
//
9088
// TODO: have 3 tags plot_impl(): iterable, callable and contiguous range.
9189
plt::plot(span(t, 4), span(data, 8), "", x, y, "b", u, v, "r");
90+
plt::grid(true);
91+
plt::title("Variadic templates recursion, span first (copy)");
92+
plt::show();
9293

9394
// This resolves to plot(contiguous_range) and does not copy data.
94-
// plt::plot(x, y, "b", u, v, "r", span(t, 4), span(data, 8));
95+
plt::plot(x, y, "b", u, v, "r", span(t, 4), span(data, 8));
96+
plt::grid(true);
97+
plt::title("Variadic templates recursion, span last (passthrough)");
98+
plt::show();
99+
95100
#else
101+
102+
// no C-arrays
103+
cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << ": "
104+
<< "No support for C-style arrays in C++ <= 17" << endl;
105+
96106
plt::plot(x, y, "b", u, v, "r");
97-
#endif
98107
plt::grid(true);
99108
plt::title("Variadic templates recursion");
100109
plt::show();
101110

111+
#endif
112+
102113
plt::detail::_interpreter::kill();
103114

104115
return 0;

matplotlibcpp.h

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,46 @@ bool plot(const ContainerX& x, const ContainerY& y, const std::string& fmt="")
477477
npy_intp xsize=x.size();
478478
npy_intp yrows=xsize, ycols=y.size()/x.size();
479479
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!
482520

483521
PyObject* pystring = PyString_FromString(fmt.c_str());
484522

@@ -510,8 +548,16 @@ bool plot(const ContainerX& x, const ContainerY& y,
510548
npy_intp xsize=x.size();
511549
npy_intp yrows=xsize, ycols=y.size()/x.size();
512550
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!
515561

516562
// construct positional args
517563
PyObject* args = PyTuple_New(2);
@@ -2995,11 +3041,11 @@ struct plot_impl<std::false_type>
29953041
PyObject* xarray =
29963042
PyArray_New(&PyArray_Type,
29973043
1, &xsize, xtype, nullptr, nullptr,
2998-
0, NPY_ARRAY_DEFAULT, nullptr);
3044+
0, NPY_ARRAY_FARRAY, nullptr);
29993045
PyObject* yarray =
30003046
PyArray_New(&PyArray_Type,
30013047
2, ysize, ytype, nullptr, nullptr,
3002-
0, NPY_ARRAY_DEFAULT, nullptr);
3048+
0, NPY_ARRAY_FARRAY, nullptr); // column major!
30033049
PyObject* pystring = PyString_FromString(format.c_str());
30043050

30053051
// fill the data

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy