@@ -61,6 +61,7 @@ struct _interpreter {
61
61
PyObject *s_python_function_hist;
62
62
PyObject *s_python_function_imshow;
63
63
PyObject *s_python_function_scatter;
64
+ PyObject *s_python_function_boxplot;
64
65
PyObject *s_python_function_subplot;
65
66
PyObject *s_python_function_subplot2grid;
66
67
PyObject *s_python_function_legend;
@@ -197,6 +198,7 @@ struct _interpreter {
197
198
s_python_function_fill_between = safe_import (pymod, " fill_between" );
198
199
s_python_function_hist = safe_import (pymod," hist" );
199
200
s_python_function_scatter = safe_import (pymod," scatter" );
201
+ s_python_function_boxplot = safe_import (pymod," boxplot" );
200
202
s_python_function_subplot = safe_import (pymod, " subplot" );
201
203
s_python_function_subplot2grid = safe_import (pymod, " subplot2grid" );
202
204
s_python_function_legend = safe_import (pymod, " legend" );
@@ -326,6 +328,27 @@ PyObject* get_2darray(const std::vector<::std::vector<Numeric>>& v)
326
328
return reinterpret_cast <PyObject *>(varray);
327
329
}
328
330
331
+ // sometimes, for labels and such, we need string arrays
332
+ PyObject * get_array (const std::vector<std::string>& strings)
333
+ {
334
+ PyObject* list = PyList_New (strings.size ());
335
+ for (std::size_t i = 0 ; i < strings.size (); ++i) {
336
+ PyList_SetItem (list, i, PyString_FromString (strings[i].c_str ()));
337
+ }
338
+ return list;
339
+ }
340
+
341
+ // not all matplotlib need 2d arrays, some prefer lists of lists
342
+ template <typename Numeric>
343
+ PyObject* get_listlist (const std::vector<std::vector<Numeric>>& ll)
344
+ {
345
+ PyObject* listlist = PyList_New (ll.size ());
346
+ for (std::size_t i = 0 ; i < ll.size (); ++i) {
347
+ PyList_SetItem (listlist, i, get_array (ll[i]));
348
+ }
349
+ return listlist;
350
+ }
351
+
329
352
#else // fallback if we don't have numpy: copy every element of the given vector
330
353
331
354
template <typename Numeric>
@@ -698,6 +721,62 @@ bool scatter(const std::vector<NumericX>& x,
698
721
return res;
699
722
}
700
723
724
+ template <typename Numeric>
725
+ bool boxplot (const std::vector<std::vector<Numeric>>& data,
726
+ const std::vector<std::string>& labels = {},
727
+ const std::unordered_map<std::string, std::string> & keywords = {})
728
+ {
729
+ PyObject* listlist = get_listlist (data);
730
+ PyObject* args = PyTuple_New (1 );
731
+ PyTuple_SetItem (args, 0 , listlist);
732
+
733
+ PyObject* kwargs = PyDict_New ();
734
+
735
+ // kwargs needs the labels, if there are (the correct number of) labels
736
+ if (!labels.empty () && labels.size () == data.size ()) {
737
+ PyDict_SetItemString (kwargs, " labels" , get_array (labels));
738
+ }
739
+
740
+ // take care of the remaining keywords
741
+ for (const auto & it : keywords)
742
+ {
743
+ PyDict_SetItemString (kwargs, it.first .c_str (), PyString_FromString (it.second .c_str ()));
744
+ }
745
+
746
+ PyObject* res = PyObject_Call (detail::_interpreter::get ().s_python_function_boxplot , args, kwargs);
747
+
748
+ Py_DECREF (args);
749
+ Py_DECREF (kwargs);
750
+
751
+ if (res) Py_DECREF (res);
752
+
753
+ return res;
754
+ }
755
+
756
+ template <typename Numeric>
757
+ bool boxplot (const std::vector<Numeric>& data,
758
+ const std::unordered_map<std::string, std::string> & keywords = {})
759
+ {
760
+ PyObject* vector = get_array (data);
761
+ PyObject* args = PyTuple_New (1 );
762
+ PyTuple_SetItem (args, 0 , vector);
763
+
764
+ PyObject* kwargs = PyDict_New ();
765
+ for (const auto & it : keywords)
766
+ {
767
+ PyDict_SetItemString (kwargs, it.first .c_str (), PyString_FromString (it.second .c_str ()));
768
+ }
769
+
770
+ PyObject* res = PyObject_Call (detail::_interpreter::get ().s_python_function_boxplot , args, kwargs);
771
+
772
+ Py_DECREF (args);
773
+ Py_DECREF (kwargs);
774
+
775
+ if (res) Py_DECREF (res);
776
+
777
+ return res;
778
+ }
779
+
701
780
template <typename Numeric>
702
781
bool bar (const std::vector<Numeric> & x,
703
782
const std::vector<Numeric> & y,
0 commit comments