template to handle strings on x axis w/ legend - #254
Conversation
bool named_plot(const std::string& name, const std::vector<std::string>& x, const std::vector<Numeric>& y, const std::string& format = "") { .. }
added template to be able to have named legend plotting: ex: time(x) vs signal(y) with legend. Prior to this change, the current library implements simple plots with no legends and with this change the above should be possible.lava
commented
Apr 2, 2021
Thanks for contributing! I'm a bit confused if/why this works, it's not mentioned on the matplotlib docs that Would you mind adding an example that uses this function, so I can check out that this works as it should? |
im-hashim
commented
Apr 3, 2021
via email
Hi,
This is needed as Python can handle string conversions easily for some
users.. But to look forward to similar functionality in CPP, we sometimes
need texts / name plots or here in my case I wanted to have time in
"<Month>< >Year" format on the X-Axis.
I used it to represent peak speed reached per month for a vehicle using:
plot::named_plot("Vehicle speed (max)", mon_yy, veh_spd); //
where mon_yy and veh_spd are vectors of which mon_yy is a mix of
string and numbers.
The resultant looks similar to this:
[image: image.png]
The current implementation will not accept spaces or characters as
vectors. Hence just prototyped a simple template for strings.
I hope this helps!
Thank you! …On Fri, Apr 2, 2021 at 4:40 PM Benno Evers ***@***.***> wrote:
Thanks for contributing!
I'm a bit confused if/why this works, it's not mentioned on the matplotlib
docs that x can be a vector of strings:
https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html
Would you mind adding an example that uses this function, so I can check
out that this works as it should?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#254 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AKKMLTO5PKJBV7RQV6QFXY3TGYTS7ANCNFSM4YPAYPNQ>
.
|
im-hashim
commented
Mar 7, 2023
@lava this is open for more than 2 years. if my response answers your question, can you please merge my pull request ? |
im-hashim
commented
Apr 16, 2026
Thanks for the review and for asking for a concrete example. I added a small runnable example at:
I also added a short README usage note and constraints under the examples section. Expected behavior:
|
There was a problem hiding this comment.
Pull request overview
Adds an API/example intended to support named_plot() with std::vector<std::string> x-axis values so users can plot categorical/string x-values while still using legend labels.
Changes:
- Introduces a new
named_plotoverload forx: std::vector<std::string>inmatplotlibcpp.h. - Adds a new example demonstrating string x-axis + legend plotting.
- Documents the new example/usage in the README.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| matplotlibcpp.h | Adds a named_plot overload for string x-values (and accidentally adds a broken named_semilogx declaration). |
| examples/string_xaxis_named_plot.cpp | New runnable example plotting categorical month labels with a legend and saving an image. |
| README.md | Adds documentation snippet and build command for the new example. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| String x-axis labels with legend: | ||
| ```cpp | ||
| #include "../matplotlibcpp.h" | ||
| #include <string> | ||
| #include <vector> | ||
| namespace plt = matplotlibcpp; | ||
| int main() | ||
| { | ||
| std::vector<std::string> x = {"Jan 2021", "Feb 2021", "Mar 2021"}; | ||
| std::vector<double> y = {58.4, 61.2, 63.9}; | ||
| plt::named_plot("Vehicle speed (max)", x, y, "o-"); | ||
| plt::legend(); | ||
| plt::save("./string_xaxis_named_plot.png"); | ||
| } | ||
| ``` | ||
| g++ examples/string_xaxis_named_plot.cpp -std=c++11 -I. -I/usr/include/python2.7 -lpython2.7 | ||
| Expected behavior and constraints: | ||
| - `x` is a `std::vector<std::string>` and `y` is numeric with matching length. | ||
| - String labels are mapped as categorical x values by matplotlib. | ||
| - Use `plt::save(...)` for headless runs or `plt::show()` for interactive runs. | ||
There was a problem hiding this comment.
The PR description/README section implies legends (and/or string x-axis support) are newly enabled, but the library already provides named_plot(...) for legend labels and detail::get_array(std::vector<std::string>) for string data. Consider updating the README text to clarify what limitation is actually being addressed (e.g., providing a dedicated overload), or remove the claim that this capability was previously unavailable.
| template<typename Numeric> | ||
| bool named_semilogx(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "") | ||
There was a problem hiding this comment.
There is an incomplete named_semilogx template declaration here (no function body and no trailing ;). This will not compile and also duplicates the existing named_semilogx template immediately below—remove this line or provide a proper definition/forward declaration ending with ;.
| template<typenameNumeric> | |
| boolnamed_semilogx(conststd::string&name, conststd::vector<Numeric>&x, conststd::vector<Numeric>&y, conststd::string&format="") |
| PyObject* kwargs = PyDict_New(); | ||
| PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); | ||
There was a problem hiding this comment.
PyDict_SetItemString does not steal a reference to the value; passing the temporary result of PyString_FromString(...) here leaks a reference. Store the created Python string in a local PyObject*, insert it into kwargs, then Py_DECREF it after insertion (same pattern as used elsewhere when the API doesn’t steal refs).
| bool named_plot(const std::string& name, const std::vector<std::string>& x, const std::vector<Numeric>& y, const std::string& format = "") | ||
| { | ||
| detail::_interpreter::get(); | ||
| PyObject* kwargs = PyDict_New(); | ||
| PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); | ||
| PyObject* xarray = detail::get_array(x); | ||
| PyObject* yarray = detail::get_array(y); | ||
| PyObject* pystring = PyString_FromString(format.c_str()); | ||
| PyObject* plot_args = PyTuple_New(3); | ||
| PyTuple_SetItem(plot_args, 0, xarray); | ||
| PyTuple_SetItem(plot_args, 1, yarray); | ||
| PyTuple_SetItem(plot_args, 2, pystring); | ||
| PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); | ||
| Py_DECREF(kwargs); | ||
| Py_DECREF(plot_args); | ||
| if (res) Py_DECREF(res); | ||
| return res; | ||
| } | ||
| template<typename Numeric> |
There was a problem hiding this comment.
This overload appears redundant: the existing named_plot template taking std::vector<NumericX> already works with NumericX = std::string because detail::get_array(const std::vector<std::string>&) is defined. Keeping both copies increases maintenance surface; consider removing this overload and relying on the generic template (or factoring shared logic into a helper).
| boolnamed_plot(conststd::string&name, conststd::vector<std::string>&x, conststd::vector<Numeric>&y, conststd::string&format="") | |
| { | |
| detail::_interpreter::get(); | |
| PyObject*kwargs=PyDict_New(); | |
| PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); | |
| PyObject*xarray=detail::get_array(x); | |
| PyObject*yarray=detail::get_array(y); | |
| PyObject*pystring=PyString_FromString(format.c_str()); | |
| PyObject*plot_args=PyTuple_New(3); | |
| PyTuple_SetItem(plot_args, 0, xarray); | |
| PyTuple_SetItem(plot_args, 1, yarray); | |
| PyTuple_SetItem(plot_args, 2, pystring); | |
| PyObject*res=PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); | |
| Py_DECREF(kwargs); | |
| Py_DECREF(plot_args); | |
| if (res) Py_DECREF(res); | |
| returnres; | |
| } | |
| template<typenameNumeric> |
bool named_plot(const std::string& name, const std::vectorstd::string& x, const std::vector& y, const std::string& format = "") { .. }
added template to be able to have named legend plotting: ex: time(x) vs signal(y) with legend.
Prior to this change, the current library implements simple plots with no legends and with this change the above should be possible.