Hi all, I've just open sourced the gnuplot interface I built as a small part of the product for my current employer. It's called nimgnuplot. It supports Datamancer dataframes. With it, you build scripts incrementally by adding commands and data to a stateful object, then execute the accumulated script. It assumes byte outputs such as png and svg images. This architecture allows one to isolate and build multiple plots independently from each other in rapid succession, which I need to do for the reporting I generate.
You can find it at https://github.com/nervecenter/nimgnuplot, and install it with Nimble using that link. I've also sent a pull request to the Nimble package listing, so it'll be available as nimgnuplot.
I'm open to comments, suggestions, and pull requests. Hopefully it's simple and unopinionated enough; the only strong opinions I have going in are Datamancer as the primary data input, and image bytes as the primary data output.
Here's an example, and you can see the resulting plot in the README and the examples directory.
import std/sugar
import std/tables
import std/strformat
import datamancer
import nimgnuplot
const
COL_COLORS* = {
"foo": "red",
"bar": "web-green",
"baz": "blue",
}.to_table
COL_NAMES* = {
"foo": "Fooregard",
"bar": "Barrington",
"baz": "Bazarang",
}.to_table
COL_POINT_TYPES* = {
"foo": 7,
"bar": 9,
"baz": 5,
}.to_table
var g = initGnuplotScript()
g.cmd """
set terminal svg size 850,500 dynamic background rgb 'white'
set style fill solid 1.0
set title 'Foo, Bar, \& Baz by Day' font ',20'
set xlabel 'Day' center
set ylabel 'Amount' center rotate by 90
set key at graph 0.5,1.04 horizontal center width -2
set border lw 2
set yrange [0:80]
set xtics nomirror
set ytics nomirror
"""
let colHeaders = g.addData("foobarbaz", readCsv("foobarbaz.csv"))
let plotElements = collect:
for col in colHeaders[1 .. ^1]:
&"u 'day':'{col}' w linespoints pt {COL_POINT_TYPES[col]} ps 0.5 lc rgb '{COL_COLORS[col]}' title '{COL_NAMES[col]}'"
g.plotData("foobarbaz", plotElements)
let svgBytes = g.execute()
writeFile("foobarbaz.svg", svgBytes)
make a concept that requires a toCSVStr proc
this sounds like a rube-goldberg machine .. why not just take the csv as an input string?
I mean having both wouldn't harm, but I would choose 2. regardless.