template to handle strings on x axis w/ legend#254
template to handle strings on x axis w/ legend#254im-hashim wants to merge 3 commits intolava:masterfrom
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.
|
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? |
|
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>
.
|
|
@lava this is open for more than 2 years. if my response answers your question, can you please merge my pull request ? |
|
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<typename Numeric> | |
| bool named_semilogx(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::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).
| 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> |
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.