This is a text-only version of the following page on https://raymii.org:
---
Title       :   GNUplot tips for nice looking charts from a CSV file
Author      :   Remy van Elst
Date        :   06-07-2019
URL         :   https://raymii.org/s/tutorials/GNUplot_tips_for_nice_looking_charts_from_a_CSV_file.html
Format      :   Markdown/HTML
---



Recently I had to do some testing which resulted in a lot of log data. Looking
at a bunch of text is not the same as seeing things graphically, this particular
logdata was perfect to put in a graph. My goto [favorite tool][1] for graphs and
charts is [gnuplot][2]. Not only is it very extensible, it is also reproducable.
Give me a configfile and command over "do this, then this and then such and
such" in Excel to get a consistent result. In this article I'll give tips for
using gnuplot, which include:

- Parsing a CSV file
- Parsing the first column as a date/time
- Using the first CSV column as title
- Using a second Y axis
- Using multiple environment variables in gnuplot
- Styling (grid, line type, colour, thickness)
- Rotating axis labels
- Creating an A4 PDF output file

I've got an article published [here][1] where you can read howto make a
bar-chart (histogram) with gnuplot. The data in this article is masked, but that
doesn't matter for the gnuplot results.


[1]: https://raymii.org/s/software/Bind-GNUPlot-DNS-Bar-Graph.html
[2]: http://gnuplot.org



<p class="ad"> <b>Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:</b><br><br> <a href="https://leafnode.nl">I'm developing an open source monitoring app called  Leaf Node Monitoring, for windows, linux & android. Go check it out!</a><br><br> <a href="https://github.com/sponsors/RaymiiOrg/">Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.</a><br><br> <a href="https://www.digitalocean.com/?refcode=7435ae6b8212">You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $100 credit for 60 days. </a><br><br> </p>


I'm using gnuplot 5.2 on Ubuntu 18.04, installed via the repository. You can
find my example CSV data at the bottom of this article. Save it as `plot.csv`.
The article will go over the different topics step by step. The data is from
another piece of software I've written and contains extra information, but that
is prefixed with a hash (`#`). Not valid CSV, but we'll use that inside gnuplot
to append some data to the title of the graph.

Here is a picture of the finished result we're working towards:

![gnuplot][3]

[3]: https://raymii.org/s/inc/img/gnuplot-1.png

### The basics, parsing a CSV file

Let's start with the basic setup and command. Create a file named
`example.gnuplot` in the same folder as your csv file and put the following in
there:

   set datafile separator ','
   plot plot.csv using 1:2 with lines, '' using 1:3 with lines

The first line tells gnuplot to use a comma instead of whitespace to seperate
the data (thus parsing the csv).

Run gnuplot, with `-p` to make the window persist:

   gnuplot -p example.gnuplot

Result:

![gnuplot][4]

[4]: https://raymii.org/s/inc/img/gnuplot-2.png

Okay, not much of what we want here. But, if you do see something like this
picture, you know your setup is correct. Let's continue on.

### Using the first column as x axis date time

The first column of our CSV file contains the date and time in an ISO8601
format. Let's tell gnuplot to use the first column as x axis datetime and
specify the correct format. Update your gnuplot file:

   set datafile separator ','

   set xdata time # tells gnuplot the x axis is time data
   set timefmt "%Y-%m-%dT%H:%M:%S" # specify our time string format
   set format x "%H:%M:%S" # otherwise it will show only MM:SS
   plot plot.csv using 1:2 with lines, '' using 1:3 with lines

Command:

   gnuplot -p example.gnuplot

Result:

![gnuplot][5]

[5]: https://raymii.org/s/inc/img/gnuplot-3.png

That looks more like it. The basics are there, our two lines on the Y axis and
the datetime on the X axis. But the graph is still a bit hard to read. The
legend has the plot commands, there is no grid and we're missing our second Y
axis. Continue on.

### Using the first CSV column as title

The CSV file has column headers in the first line:

   "datetime","targetValue","measuredValue","secondYAxisValue",

Let's tell gnuplot to use those. We'll also add a label to the X and Y axis:

   set datafile separator ','
   set xdata time
   set timefmt "%Y-%m-%dT%H:%M:%S"

   set key autotitle columnhead # use the first line as title
   set ylabel "First Y Units" # label for the Y axis
   set xlabel 'Time' # label for the X axis

   plot plot.csv using 1:2 with lines, '' using 1:3 with lines

Command:

   gnuplot -p example.gnuplot

Result:

![gnuplot][6]

[6]: https://raymii.org/s/inc/img/gnuplot-4.png

The legend is correct, and we have axis labels. Time to work on that second Y axis.

### Using a second Y axis

The last column in our CSV file is a different unit but influences the other
two. We want to show that on a second axis.

   set datafile separator ','
   set xdata time
   set timefmt "%Y-%m-%dT%H:%M:%S"
   set key autotitle columnhead
   set ylabel "First Y Units"
   set xlabel 'Time'

   set y2tics # enable second axis
   set ytics nomirror # dont show the tics on that side
   set y2label "Second Y Axis Value" # label for second axis

   plot plot.csv using 1:2 with lines, '' using 1:3 with lines, '' using 1:4 with lines axis x1y2 # new plot command

Command:

   gnuplot -p example.gnuplot

Result:

![gnuplot][7]

[7]: https://raymii.org/s/inc/img/gnuplot-5.png

#### Second Y axis starting from 0?

As you can see, the second Y axis doesn't start from zero. That scale is
relative to the lowest value in your CSV. If you do want it starting from zero,
use the `set y2range` option:

   set y2range [0:]

The first number (the zero) is the starting range. The second number is the end
range. I'm leaving the end range blank. Same goes for `yrange` or `xrange`.

With the axis starting from zero, the graph looks like this:

![gnuplot][8]

[8]: https://raymii.org/s/inc/img/gnuplot-6.png


### Using multiple environment variables in gnuplot

My situation is that I get about a hundred of these logfiles a day, times 25
machines. I'm not going to do all of the work by hand, so by using a few scripts
we can programmatically generate these graphs.

Also, in the CSV file we've embedded two lines, `header` and `footer`, which
contain information I want in the graph title. With the `-e` option you can pass
(environment) variables to gnuplot:

   gnuplot -p -e "titel='${PLOTTITLE}'; filename='${FILE}';" example.gnuplot

Do note that the environment variables are surrounded by single quotes. This is
required for textual values. Also note the semicolon to seperate multiple
values.

Inside gnuplot these are accessible:

   set title titel #our var "titel" = ${PLOTTITLE}
   plot filename using 1:2 with lines, '' using 1:3 with lines, '' using 1:4 with lines axis x1y2 #our var "filename" = ${FILE}

My complete command (to set the filename and title) includes a bit of parsing to
get the header and footer inside the `title` variable:

   FILE="plot.csv";
   PLOTTITLE=$(grep -i -e header -e footer $(echo $FILE) | sed s/\"\//g | sed s/\#//g);
   gnuplot -e "titel='${PLOTTITLE}'; filename='${FILE}';" example.gnuplot

The graph with the dynamic title:

![gnuplot][9]

[9]: https://raymii.org/s/inc/img/gnuplot-7.png


### Styling (grid, line type, colour, thickness)

The graph up to this point contains all the data we want in it. The rest of
these tips will focus on styling and presentation.

I'm going to talk about the linetype option for a bit first. In older gnuplot
versions, each terminal type provided a set of distinct `linetypes` that could
differ in color, in thickness, in dot/dash pattern, or in some combination of
color and dot/dash. These colors and patterns were not guaranteed to be
consistent across different terminal types although most used the color sequence
red/green/blue/magenta/cyan/yellow.By default gnuplot version 5 uses a
terminal-independent sequence of 8 colors. A terminal type can be the program
itself, a png file, a postscript file, etc.

#### Grid

We'll start off with the grid. I'm defining a new linestyle with a grey colour
and a smaller line. We're combining that with a smaller `x/ytics` size to get a
smaller grid.

   set datafile separator ','
   set xdata time
   set timefmt "%Y-%m-%dT%H:%M:%S"
   set key autotitle columnhead
   set ylabel "First Y Units"
   set xlabel 'Time'
   set y2tics
   set ytics nomirror
   set y2label "Second Y Axis Value"

   set style line 100 lt 1 lc rgb "grey" lw 0.5 # linestyle for the grid
   set grid ls 100 # enable grid with specific linestyle
   set ytics 0.5 # smaller ytics
   set xtics 1   # smaller xtics

   plot filename using 1:2 with lines, '' using 1:3 with lines, '' using 1:4 with lines axis x1y2

Gnuplot command:

   FILE="plot.csv";
   PLOTTITLE=$(grep -i -e header -e footer $(echo $FILE) | sed s/\"\//g | sed s/\#//g);
   gnuplot -e "titel='${PLOTTITLE}'; filename='${FILE}';" example.gnuplot

Example output:

![gnuplot][10]

[10]: https://raymii.org/s/inc/img/gnuplot-8.png

#### Line colour and thickness

The default colours are a tad bit uneasy to distinguish from one another. We can
turn them into a colour of our choosing by specifying the line style. I'm also
going to make the lines a bit thicker with the `lw` (line width) command.

   set datafile separator ','
   set xdata time
   set timefmt "%Y-%m-%dT%H:%M:%S"
   set key autotitle columnhead
   set ylabel "First Y Units"
   set xlabel 'Time'
   set y2tics
   set ytics nomirror
   set y2label "Second Y Axis Value"
   set style line 100 lt 1 lc rgb "grey" lw 0.5
   set grid ls 100
   set ytics 0.5
   set xtics 1

   set style line 101 lw 3 lt rgb "#f62aa0" # style for targetValue (1) (pink)
   set style line 102 lw 3 lt rgb "#26dfd0" # style for measuredValue (2) (light blue)
   set style line 103 lw 4 lt rgb "#b8ee30" # style for secondYAxisValue (3) (limegreen)

   plot filename using 1:2 with lines ls 101, '' using 1:3 with lines ls 102, '' using 1:4 with lines axis x1y2 ls 103 # new plotcommand

Gnuplot command:

   FILE="plot.csv";
   PLOTTITLE=$(grep -i -e header -e footer $(echo $FILE) | sed s/\"\//g | sed s/\#//g);
   gnuplot -e "titel='${PLOTTITLE}'; filename='${FILE}';" example.gnuplot

Example output:

![gnuplot][11]

[11]: https://raymii.org/s/inc/img/gnuplot-9.png

The thicker lines combined with the narrow grid make it more easy to see where
the two lines overlap or are near one another.


### Rotating axis labels and placement of the legend

We're now nearing our final beautiful graph. A few things left to change. The
legend is in the way of the upper second axis and the X axis values are
overlapping eachother. Do note that the legend can be placed outside the graph,
but that will lower the effective width of your graph. I wasn't able to figure
out how to place the legend outside of the graph at the bottom, where it
wouldn't cost horizontal screen space.


   set datafile separator ','
   set xdata time
   set timefmt "%Y-%m-%dT%H:%M:%S"
   set key autotitle columnhead
   set ylabel "First Y Units"
   set xlabel 'Time'
   set y2tics
   set ytics nomirror
   set y2label "Second Y Axis Value"
   set style line 100 lt 1 lc rgb "grey" lw 0.5
   set grid ls 100
   set ytics 0.5
   set xtics 1
   set style line 101 lw 3 lt rgb "#f62aa0"
   set style line 102 lw 3 lt rgb "#26dfd0"
   set style line 103 lw 4 lt rgb "#b8ee30"

   set xtics rotate # rotate labels on the x axis
   set key right center # legend placement

   plot filename using 1:2 with lines ls 101, '' using 1:3 with lines ls 102, '' using 1:4 with lines axis x1y2 ls 103

Gnuplot command:

   FILE="plot.csv";
   PLOTTITLE=$(grep -i -e header -e footer $(echo $FILE) | sed s/\"\//g | sed s/\#//g);
   gnuplot -e "titel='${PLOTTITLE}'; filename='${FILE}';" example.gnuplot

Example output:

![gnuplot][12]

[12]: https://raymii.org/s/inc/img/gnuplot-10.png

Here is output with the `set key outside right bottom` option, so you can see
what I mean with the screen realestate:

![gnuplot][13]

[13]: https://raymii.org/s/inc/img/gnuplot-11.png

This is the graph we want. If you want to have this output to a PNG file
automatically, add the following options:

   set terminal pngcairo size 800,600 enhanced font 'Segoe UI,10'
   set output 'example.png'

If you want to have a gnuplot window with a different size and font:

   set terminal wxt size 800,600 enhanced font 'Segoe UI,10' persist

The graph is finished and styled the way we want. I need to have these archived,
with the other scripts I wrote the output files all go into a folder, but I
needed PDF's for archiving and printing. We'll use an external tool for that.

### Creating an A4 PDF output file

First install the `ghostscript` package:

   sudo apt-get install ghostscript

This contains the tool we want (`ps2pdf`). gnuplot can output to `.ps`
(postscript), which we can convert to PDF and automagically get the correct page
format.

Add the following option to get the postscript output:

   set terminal postscript enhanced color landscape 'Arial' 12
   set output 'example.ps'
   set size ratio 0.71 # for the A4 ratio

The gnuplot command:

   FILE="plot.csv";
   PLOTTITLE=$(grep -i -e header -e footer $(echo $FILE) | sed s/\"\//g | sed s/\#//g);
   gnuplot -e "titel='${PLOTTITLE}'; filename='${FILE}';" example.gnuplot;

After that, use this command to convert the postscript file to a PDF:

   ps2pdf -sPAGESIZE=a4 example.ps example_a4_$FILE.pdf;

If you want black and white instead of colour, omit the `color enhanced` part
from the `set terminal` line.

This should give you a PDF file which you can print directly. Below is the CSV
file I used.

### The CSV file

   "datetime","targetValue","measuredValue","secondYAxisValue",
   #header: information you want to put in the title.
   "2019-07-04T07:48:13.377Z","1.76087","0.01","7975"
   "2019-07-04T07:48:13.545Z","1.76087","13.431","7975"
   "2019-07-04T07:48:13.744Z","1.76087","13.431","7975"
   "2019-07-04T07:48:13.945Z","1.76087","12.21","7975"
   "2019-07-04T07:48:14.170Z","1.76087","11.009","7975"
   "2019-07-04T07:48:14.344Z","1.76087","8.61","7975"
   "2019-07-04T07:48:14.545Z","1.76087","5.643","7996"
   "2019-07-04T07:48:14.751Z","1.76087","4.447","8048"
   "2019-07-04T07:48:14.949Z","1.76087","3.649","8086"
   "2019-07-04T07:48:15.158Z","1.76087","3.198","8137"
   "2019-07-04T07:48:15.345Z","1.76087","2.919","8183"
   "2019-07-04T07:48:15.543Z","1.76087","2.821","8199"
   "2019-07-04T07:48:15.744Z","1.76087","2.821","8248"
   "2019-07-04T07:48:15.947Z","1.76087","2.697","8298"
   "2019-07-04T07:48:16.145Z","1.76087","2.543","8337"
   "2019-07-04T07:48:16.343Z","1.76087","2.348","8349"
   "2019-07-04T07:48:16.544Z","1.76087","2.348","8361"
   "2019-07-04T07:48:16.744Z","1.76087","2.253","8386"
   "2019-07-04T07:48:16.945Z","1.76087","2.216","8397"
   "2019-07-04T07:48:17.144Z","1.76087","2.216","8413"
   "2019-07-04T07:48:17.344Z","1.76087","2.159","8435"
   "2019-07-04T07:48:17.544Z","1.76087","2.125","8435"
   "2019-07-04T07:48:17.744Z","1.76087","2.125","8456"
   "2019-07-04T07:48:17.948Z","1.76087","2.079","8474"
   "2019-07-04T07:48:18.145Z","1.76087","2.079","8474"
   "2019-07-04T07:48:18.343Z","1.76087","2.022","8487"
   "2019-07-04T07:48:18.546Z","1.76087","2.004","8490"
   "2019-07-04T07:48:18.744Z","1.76087","2.004","8502"
   "2019-07-04T07:48:18.945Z","1.76087","1.981","8515"
   "2019-07-04T07:48:19.169Z","1.76087","1.981","8515"
   "2019-07-04T07:48:19.345Z","1.76087","1.952","8526"
   "2019-07-04T07:48:19.544Z","1.76087","1.952","8526"
   "2019-07-04T07:48:19.765Z","1.76087","1.957","8539"
   "2019-07-04T07:48:19.963Z","1.76087","1.913","8546"
   "2019-07-04T07:48:20.149Z","1.76087","1.913","8546"
   "2019-07-04T07:48:20.343Z","1.76087","1.902","8554"
   "2019-07-04T07:48:20.545Z","1.76087","1.902","8554"
   "2019-07-04T07:48:20.744Z","1.76087","1.855","8558"
   "2019-07-04T07:48:20.943Z","1.76087","1.855","8558"
   "2019-07-04T07:48:21.146Z","1.76087","1.762","8553"
   "2019-07-04T07:48:21.344Z","1.76087","1.762","8553"
   "2019-07-04T07:48:21.545Z","1.76087","1.824","8560"
   "2019-07-04T07:48:21.743Z","1.76087","1.981","8573"
   "2019-07-04T07:48:21.946Z","1.76087","1.981","8583"
   "2019-07-04T07:48:22.147Z","1.76087","1.946","8593"
   "2019-07-04T07:48:22.344Z","1.76087","1.946","8593"
   "2019-07-04T07:48:22.545Z","1.76087","1.897","8599"
   "2019-07-04T07:48:22.744Z","1.76087","1.897","8599"
   "2019-07-04T07:48:22.944Z","1.76087","1.881","8606"
   "2019-07-04T07:48:23.157Z","1.76087","1.86","8611"
   "2019-07-04T07:48:23.345Z","1.76087","1.86","8611"
   "2019-07-04T07:48:23.545Z","1.76087","1.845","8616"
   "2019-07-04T07:48:23.745Z","1.76087","1.845","8616"
   "2019-07-04T07:48:23.945Z","1.76087","1.865","8624"
   "2019-07-04T07:48:24.163Z","1.76087","1.865","8624"
   "2019-07-04T07:48:24.344Z","1.76087","1.875","8632"
   "2019-07-04T07:48:24.546Z","1.76087","1.875","8632"
   "2019-07-04T07:48:24.761Z","1.76087","1.865","8638"
   "2019-07-04T07:48:24.947Z","1.76087","1.86","8640"
   "2019-07-04T07:48:25.148Z","1.76087","1.86","8644"
   "2019-07-04T07:48:25.344Z","1.76087","1.85","8649"
   "2019-07-04T07:48:25.545Z","1.76087","1.85","8649"
   "2019-07-04T07:48:25.744Z","1.76087","1.86","8656"
   "2019-07-04T07:48:25.943Z","1.76087","1.86","8656"
   "2019-07-04T07:48:26.143Z","1.76087","1.8","8656"
   "2019-07-04T07:48:26.347Z","1.76087","1.8","8656"
   "2019-07-04T07:48:26.545Z","1.76087","1.79","8656"
   "2019-07-04T07:48:26.748Z","1.76087","1.79","8656"
   "2019-07-04T07:48:26.943Z","1.76087","1.753","8656"
   "2019-07-04T07:48:27.144Z","1.76087","1.753","8656"
   "2019-07-04T07:48:27.348Z","1.76087","1.758","8656"
   "2019-07-04T07:48:27.546Z","1.76087","1.758","8656"
   "2019-07-04T07:48:27.746Z","1.76087","1.73","8656"
   "2019-07-04T07:48:27.943Z","1.76087","1.739","8656"
   "2019-07-04T07:48:28.155Z","1.76087","1.739","8656"
   "2019-07-04T07:48:28.346Z","1.76087","1.744","8656"
   "2019-07-04T07:48:28.545Z","1.76087","1.744","8656"
   "2019-07-04T07:48:28.747Z","1.76087","1.739","8656"
   "2019-07-04T07:48:28.956Z","1.76087","1.739","8656"
   "2019-07-04T07:48:29.155Z","1.76087","1.713","8652"
   "2019-07-04T07:48:29.344Z","1.76087","1.713","8652"
   "2019-07-04T07:48:29.545Z","1.76087","1.739","8652"
   "2019-07-04T07:48:29.762Z","1.76087","1.739","8652"
   "2019-07-04T07:48:29.947Z","1.76087","1.735","8652"
   "2019-07-04T07:48:30.149Z","1.76087","1.735","8652"
   "2019-07-04T07:48:30.347Z","1.76087","1.717","8648"
   "2019-07-04T07:48:30.549Z","1.76087","1.717","8648"
   "2019-07-04T07:48:30.757Z","1.76087","1.704","8644"
   "2019-07-04T07:48:30.944Z","1.76087","1.704","8644"
   "2019-07-04T07:48:31.153Z","1.76087","1.704","8644"
   #footer: will be appended to title.

---

License:
All the text on this website is free as in freedom unless stated otherwise.
This means you can use it in any way you want, you can copy it, change it
the way you like and republish it, as long as you release the (modified)
content under the same license to give others the same freedoms you've got
and place my name and a link to this site with the article as source.

This site uses Google Analytics for statistics and Google Adwords for
advertisements. You are tracked and Google knows everything about you.
Use an adblocker like ublock-origin if you don't want it.

All the code on this website is licensed under the GNU GPL v3 license
unless already licensed under a license which does not allows this form
of licensing or if another license is stated on that page / in that software:

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

Just to be clear, the information on this website is for meant for educational
purposes and you use it at your own risk. I do not take responsibility if you
screw something up. Use common sense, do not 'rm -rf /' as root for example.
If you have any questions then do not hesitate to contact me.

See https://raymii.org/s/static/About.html for details.