Browse Source

Adds boxplot, get_strarray and get_listlist

Florian Eich 5 years ago
parent
commit
52816e2f8b
1 changed files with 79 additions and 0 deletions
  1. 79 0
      matplotlibcpp.h

+ 79 - 0
matplotlibcpp.h

@@ -61,6 +61,7 @@ struct _interpreter {
     PyObject *s_python_function_hist;
     PyObject *s_python_function_imshow;
     PyObject *s_python_function_scatter;
+    PyObject *s_python_function_boxplot;
     PyObject *s_python_function_subplot;
     PyObject *s_python_function_subplot2grid;
     PyObject *s_python_function_legend;
@@ -197,6 +198,7 @@ private:
         s_python_function_fill_between = safe_import(pymod, "fill_between");
         s_python_function_hist = safe_import(pymod,"hist");
         s_python_function_scatter = safe_import(pymod,"scatter");
+        s_python_function_boxplot = safe_import(pymod,"boxplot");
         s_python_function_subplot = safe_import(pymod, "subplot");
         s_python_function_subplot2grid = safe_import(pymod, "subplot2grid");
         s_python_function_legend = safe_import(pymod, "legend");
@@ -326,6 +328,27 @@ PyObject* get_2darray(const std::vector<::std::vector<Numeric>>& v)
     return reinterpret_cast<PyObject *>(varray);
 }
 
+// sometimes, for labels and such, we need string arrays
+PyObject * get_array(const std::vector<std::string>& strings)
+{
+  PyObject* list = PyList_New(strings.size());
+  for (std::size_t i = 0; i < strings.size(); ++i) {
+    PyList_SetItem(list, i, PyString_FromString(strings[i].c_str()));
+  }
+  return list;
+}
+
+// not all matplotlib need 2d arrays, some prefer lists of lists
+template<typename Numeric>
+PyObject* get_listlist(const std::vector<std::vector<Numeric>>& ll)
+{
+  PyObject* listlist = PyList_New(ll.size());
+  for (std::size_t i = 0; i < ll.size(); ++i) {
+    PyList_SetItem(listlist, i, get_array(ll[i]));
+  }
+  return listlist;
+}
+
 #else // fallback if we don't have numpy: copy every element of the given vector
 
 template<typename Numeric>
@@ -698,6 +721,62 @@ bool scatter(const std::vector<NumericX>& x,
     return res;
 }
 
+template<typename Numeric>
+bool boxplot(const std::vector<std::vector<Numeric>>& data,
+             const std::vector<std::string>& labels = {},
+             const std::unordered_map<std::string, std::string> & keywords = {})
+{
+    PyObject* listlist = get_listlist(data);
+    PyObject* args = PyTuple_New(1);
+    PyTuple_SetItem(args, 0, listlist);
+
+    PyObject* kwargs = PyDict_New();
+
+    // kwargs needs the labels, if there are (the correct number of) labels
+    if (!labels.empty() && labels.size() == data.size()) {
+        PyDict_SetItemString(kwargs, "labels", get_array(labels));
+    }
+
+    // take care of the remaining keywords
+    for (const auto& it : keywords)
+    {
+        PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str()));
+    }
+
+    PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_boxplot, args, kwargs);
+
+    Py_DECREF(args);
+    Py_DECREF(kwargs);
+
+    if(res) Py_DECREF(res);
+
+    return res;
+}
+
+template<typename Numeric>
+bool boxplot(const std::vector<Numeric>& data,
+             const std::unordered_map<std::string, std::string> & keywords = {})
+{
+    PyObject* vector = get_array(data);
+    PyObject* args = PyTuple_New(1);
+    PyTuple_SetItem(args, 0, vector);
+
+    PyObject* kwargs = PyDict_New();
+    for (const auto& it : keywords)
+    {
+        PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str()));
+    }
+
+    PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_boxplot, args, kwargs);
+
+    Py_DECREF(args);
+    Py_DECREF(kwargs);
+
+    if(res) Py_DECREF(res);
+
+    return res;
+}
+
 template <typename Numeric>
 bool bar(const std::vector<Numeric> &               x,
          const std::vector<Numeric> &               y,