Plotting an IR Spectrum with Gnuplot
Asked Answered
L

2

5

I have an infrared spectrum for a compound of interest that I would like to plot, and I have a spectrum.dat file with all of the data points. It is of the form:

    # X  Y     
    300  100
    301  100
    302   99
    303   70
    ...
    3999  98
    4000 100

I would like to plot this using an x axis typical of IR spectra, but I am having trouble doing so. If you are unfamiliar, this is what a typical IR spectrum might look like (aside from the labels on the graph itself). Notice that the x-axis is reversed, and that it abruptly doubles its scaling above 2000 units (reciprocal centimeters). Is there a way to coerce Gnuplot into plotting my data this way? I so far have managed to come up with the following script:

    # Make an SVG of size 800x500
    set terminal svg size 800,500 fname 'CMU Sans Serif' fsize '10'
    set output 'ir.svg'
    # Color definitions
    set border linewidth 1.5
    set style line 1 lc rgb '#a0a0a0' lt 1 lw 2 pt 7 # gray
    # Format graph
    unset key
    set xlabel 'Wavenumbers'
    set ylabel 'Transmittance'
    set xrange [4000:300]
    # Plot data
    plot 'spectrum.dat' with lines ls 1

This reverses the x-axis nicely, but I can't figure out how to change the scaling in such an unusual way.

Lief answered 21/5, 2014 at 0:21 Comment(1)
Please see also gnuplot: how to set custom non linear scales if using set link and the development version of gnuplot is an option for you. The next upcoming gnuplot version 5.0 now even has a release candidate :)Orta
M
5

andyras' answer is a nice one, this is an arguably simpler (more elegant :-P) solution in terms of layout options. This should also be a more universal solution. If there are not too many tics (read below the figure if there are too many), then this could be done scaling the curve itself beyond 2000, and then adding all the tics by hand. Since I don't have IR spectrum data available I will use the dummy file "+" and plot log(x) from 4000 to 500:

xmax=4000 ; xmin = 500
pivot = 2000 ; rescfactor = 2.
rescale(x) = (x >= pivot ? x : pivot + rescfactor*(x-pivot))
set xrange [rescale(xmax):rescale(xmin)]
set xtics ("4000" 4000, "3000" 3000, "2000" 2000, \
"1500" rescale(1500), "1000" rescale(1000), "500" rescale(500))
plot "+" u (rescale($1)):(log($1)) w l

enter image description here

In your case you just substitute log($1) by 2 or whatever you're plotting.

In newer versions of gnuplot (starting from 4.4) adding the tics can be done automatically using a loop:

xmax = 4000 ; xmin = 500 ; step = 500
set xtics (sprintf("%i",xmax) rescale(xmax)) # Add the first tic by hand
set for [i=xmin:xmax-step:step] xtics add (sprintf("%i",i) rescale(i))

Starting from gnuplot 4.6 also a different for construction can be made using do for:

do for [i=xmin:xmax-step:step] {set xtics add (sprintf("%i",i) rescale(i))}
Moleskins answered 21/5, 2014 at 7:24 Comment(3)
Nice answer, the one change I might make would be to define pivot=2000; rescale(x) = ($1 >= pivot ? $1 : pivot + 2.*($1-pivot)) so you can call the function on all quantities.Kiruna
@Kiruna Changed, I also added a rescaling factor. Funnily enough, I needed to use this solution today at work for something totally unrelated...Moleskins
If you change the iteration for the xtics to use set for, it works also with version 4.4: set for [i=xmin:xmax-step:step] xtics add (sprintf("%i",i) rescale(i)). Only the do for construct was introduced with version 4.6.Orta
K
6

As a chemist I am motivated to answer...

As far as I know gnuplot doesn't easily allow for arbitrary axis scaling (unless anyone has bright ideas about how to use set link). My strategy in this kind of situation is to plot the two halves separately and have them join seamlessly:

#!/usr/bin/env gnuplot

set terminal png size 800,500
set output 'ir.png'

set xlabel 'Wavenumbers' offset 20
set ylabel 'Transmittance'

set tics out nomirror

set key bottom right

set bmargin 4

set yrange [0:1]

set multiplot layout 1,2 title 'IR Spectrum of Cholesterol'

# left half of plot
set xrange [4000:2000]
set rmargin 0
set border 7
plot 'cholesterol.txt' notitle

# right half of plot
set xrange [1999:300]
set lmargin 0
set rmargin 2
set border 13

unset xlabel
unset ylabel
unset ytics

plot 'cholesterol.txt' title 'Cholesterol'

unset multiplot

My one quibble is that the 2000 is written twice and looks bolder on my screen, but I will leave fidgeting with the tics to you.

enter image description here

Kiruna answered 21/5, 2014 at 1:23 Comment(0)
M
5

andyras' answer is a nice one, this is an arguably simpler (more elegant :-P) solution in terms of layout options. This should also be a more universal solution. If there are not too many tics (read below the figure if there are too many), then this could be done scaling the curve itself beyond 2000, and then adding all the tics by hand. Since I don't have IR spectrum data available I will use the dummy file "+" and plot log(x) from 4000 to 500:

xmax=4000 ; xmin = 500
pivot = 2000 ; rescfactor = 2.
rescale(x) = (x >= pivot ? x : pivot + rescfactor*(x-pivot))
set xrange [rescale(xmax):rescale(xmin)]
set xtics ("4000" 4000, "3000" 3000, "2000" 2000, \
"1500" rescale(1500), "1000" rescale(1000), "500" rescale(500))
plot "+" u (rescale($1)):(log($1)) w l

enter image description here

In your case you just substitute log($1) by 2 or whatever you're plotting.

In newer versions of gnuplot (starting from 4.4) adding the tics can be done automatically using a loop:

xmax = 4000 ; xmin = 500 ; step = 500
set xtics (sprintf("%i",xmax) rescale(xmax)) # Add the first tic by hand
set for [i=xmin:xmax-step:step] xtics add (sprintf("%i",i) rescale(i))

Starting from gnuplot 4.6 also a different for construction can be made using do for:

do for [i=xmin:xmax-step:step] {set xtics add (sprintf("%i",i) rescale(i))}
Moleskins answered 21/5, 2014 at 7:24 Comment(3)
Nice answer, the one change I might make would be to define pivot=2000; rescale(x) = ($1 >= pivot ? $1 : pivot + 2.*($1-pivot)) so you can call the function on all quantities.Kiruna
@Kiruna Changed, I also added a rescaling factor. Funnily enough, I needed to use this solution today at work for something totally unrelated...Moleskins
If you change the iteration for the xtics to use set for, it works also with version 4.4: set for [i=xmin:xmax-step:step] xtics add (sprintf("%i",i) rescale(i)). Only the do for construct was introduced with version 4.6.Orta

© 2022 - 2024 — McMap. All rights reserved.