First-timer with gnuplot. Essentially I'm interested in what the subject says. I already have a mechanism that works in terms of rendering the bars and I just want to add label-values on the top of each bar:
I have the following data in a file called 'data.dat':
1,Json,457054
2,MessagePack,685440
3,Cbor,1273723
I employ the following gnuplot configuration file 'plot.gp':
##
# file_path - path to the file from which the data will be read
# graphic_file_name - the graphic file name to be saved
# y_label - the desired label for y axis
# y_range_min - minimum range for values in y axis
# y_range_max - maximum range for values in y axis
# column_1 - the first column to be used in plot command
# column_2 - the second column to be used in plot command
##
# graphic will be saved as 800x600 png image file
set terminal png
# allows grid lines to be drawn on the plot
set grid
# setting the graphic file name to be saved
set output graphic_file_name
# the graphic's main title
set title "Comparison"
# since the input file is a CSV file, we need to tell gnuplot that data fields are separated by comma
set datafile separator ","
# disable key box
set key off
# label for y axis
set ylabel y_label
# range for values in y axis
set yrange[y_range_min:y_range_max]
# to avoid displaying large numbers in exponential format
set format y "%.0f"
# vertical label for x values
set xtics rotate
# set boxplots
set style fill solid
set boxwidth 0.5
# plot graphic for each line of input file
plot for [i=0:*] file_path every ::i::i using column_1:column_2:xtic(2) with boxes
And I run the following gnuplot command:
gnuplot \
-e "file_path='data.dat' " \
-e "graphic_file_name='output.png' " \
-e "y_label='y' " \
-e "y_range_min='0000000'' " \
-e "y_range_max='1500000' " \
-e "column_1=1 " \
-e "column_2=3 " \
plot.gp
I can't figure out how to use 'with labels' at the bottom of the .gp file. Any help appreciated.
Here is yet another example. There is no need to do it in a for loop. You can use the pseudocolumn 0 (check help pseudocolumns) and lc var (check help linecolor variable) for setting the color.
Code:
### plot with boxes and labels
reset session
$Data <<EOD
1,Json,457054
2,MessagePack,685440
3,Cbor,1273723
EOD
set datafile separator comma
set style fill solid 0.3
set key noautotitle
set xrange[0.5:3.5]
set yrange[0:]
set format y "%.0f"
set grid x,y
set boxwidth 0.8 relative
plot $Data u 1:3:($0+1):xtic(2) w boxes lc var, \
'' u 1:3:3 w labels offset 0,0.7
### end of code
Result:
I'm doing a gif in gnuplot, and I have my data separated in blocks. I need the points to be white except from just the first row of every data block, which would be an orange point.
Currently my code is:
#...
do for [i=0:int(STATS_blocks-1)]{
plot "positions.txt" index i pt 7 ps 0.5 lc 'white' title "t = ".((i+1)*200)." Myr"
}
As you can see, this plots every data point white, including the first row.
Edited to show variable pointsize also
If I understand your data format correctly:
set linetype 11 lc "orange"
set linetype 12 lc "white"
set style data points
do for [i=0:N] {
plot "positions.txt" index i using 1:2:(column(0)>0 ? 0.5 : 2.0):(column(0)>0 ? 12 : 11) pt 7 ps variable lc variable
}
Variable color (if used) is always taken from the very last using column. Other variable properties work back from there.
I am trying to use gnuplot 5.0 to plot a 2D array of data with no margins or borders or axes... just a 2D image (.png or .jpg) representing some data. I would like to have each array element to correspond to exactly one pixel in the image with no scaling / interpolation etc and no extra white pixels at the edges.
So far, when I try to set the margins to 0 and even using the pixels flag, I am still left with a row of white pixels on the right and top borders of the image.
How can I get just an image file with pixel-by-pixel representation of a data array and nothing extra?
gnuplot script:
#!/usr/bin/gnuplot --persist
set terminal png size 400, 200
set size ratio -1
set lmargin at screen 0
set rmargin at screen 1
set tmargin at screen 0
set bmargin at screen 1
unset colorbox
unset tics
unset xtics
unset ytics
unset border
unset key
set output "pic.png"
plot "T.dat" binary array=400x200 format="%f" with image pixels notitle
Example data from Fortran 90:
program main
implicit none
integer, parameter :: nx = 400
integer, parameter :: ny = 200
real, dimension (:,:), allocatable :: T
allocate (T(nx,ny))
T(:,:)=0.500
T(2,2)=5.
T(nx-1,ny-1)=5.
T(2,ny-1)=5.
T(nx-1,2)=5.
open(3, file="T.dat", access="stream")
write(3) T(:,:)
close(3)
end program main
Some gnuplot terminals implement "with image" by creating a separate png file containing the image and then linking to it inside the resulting plot. Using that separate png image file directly will avoid any issues of page layout, margins, etc. Here I use the canvas terminal. The plot itself is thrown away; all we keep is the png file created with the desired content.
gnuplot> set term canvas name 'myplot'
Terminal type is now 'canvas'
Options are ' rounded size 600,400 enhanced fsize 10 lw 1 fontscale 1 standalone'
gnuplot> set output '/dev/null'
gnuplot> plot "T.dat" binary array=400x200 format="%f" with image
linking image 1 to external file myplot_image_01.png
gnuplot> quit
$identify myplot_image_01.png
myplot_image_01.png PNG 400x200 400x200+0+0 8-bit sRGB 348B 0.000u 0:00.000
Don't use gnuplot.
Instead, write a script that reads your data and converts it into one of the Portable Anymap formats. Here's an example in Python:
#!/usr/bin/env python3
import math
import struct
width = 400
height = 200
levels = 255
raw_datum_fmt = '=d' # native, binary double-precision float
raw_datum_size = struct.calcsize(raw_datum_fmt)
with open('T.dat', 'rb') as f:
print("P2")
print("{} {}".format(width, height))
print("{}".format(levels))
raw_data = f.read(width * height * raw_datum_size)
for y in range(height):
for x in range(width):
raw_datum, = struct.unpack_from(raw_datum_fmt, raw_data, (y * width + x) * raw_datum_size)
datum = math.floor(raw_datum * levels) # assume a number in the range [0, 1]
print("{:>3} ".format(datum), end='')
print()
If you can modify the program which generates the data file, you can even skip the above step and instead generate the data directly in a PNM format.
Either way, you can then use ImageMagick to convert the image to a format of your choice:
./convert.py | convert - pic.png
This should be an easy task, however, apparently it's not.
The following might be a (cumbersome) solution because all other attempts failed. My suspicion is that some graphics library has an issue which you probably cannot solve as a gnuplot user.
You mentioned that ASCII matrix data is also ok. The "trick" here is to plot data with lines where the data is "interrupted" by empty lines, basically drawing single points. Check this in case you need to get your datafile 1:1 into a datablock.
However, if it is not already strange enough, it seems to work for png and gif terminal but not for pngcairo or wxt.
I guess the workaround is probably slow and inefficient but at least it creates the desired output. I'm not sure if there is a limit on size. Tested with 100x100 pixels with Win7, gnuplot 5.2.6. Comments and improvements are welcome.
Code:
### pixel image from matrix data without strange white border
reset session
SizeX = 100
SizeY = 100
set terminal png size SizeX,SizeY
set output "tbPixelImage.png"
# generate some random matrix data
set print $Data2
do for [y=1:SizeY] {
Line = ''
do for [x=1:SizeX] {
Line = Line.sprintf(" %9d",int(rand(0)*0x01000000)) # random color
}
print Line
}
set print
# print $Data2
# convert matrix data into x y z data with empty lines inbetween
set print $Data3
do for [y=1:SizeY] {
do for [x=1:SizeX] {
print sprintf("%g %g %s", x, y, word($Data2[y],x))
print ""
}
}
set print
# print $Data3
set margins 0,0,0,0
unset colorbox
unset border
unset key
unset tics
set xrange[1:SizeX]
set yrange[1:SizeY]
plot $Data3 u 1:2:3 w l lw 1 lc rgb var notitle
set output
### end of code
Result: (100x100 pixels)
(enlarged with black background):
Image with 400x200 pixels (takes about 22 sec on my 8 year old laptop).
What I ended up actually using to get what I needed even though the question / bounty asks for a gnuplot solution:
matplotlib has a function matplotlib.pyplot.imsave which does what I was looking for... i.e. plotting 'just data pixels' and no extras like borders, margins, axes, etc. Originally I only knew about matplotlib.pyplot.imshow and had to pull a lot of tricks to eliminate all the extras from the image file and prevent any interpolation/smoothing etc (and therefore turned to gnuplot at a certain point). With imsave it's fairly easy, so I'm back to using matplotlib for an easy yet still flexible (in terms of colormap, scaling, etc) solution for 'pixel exact' plots. Here's an example:
#!/usr/bin/env python3
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
nx = 400
ny = 200
data = np.fromfile('T.dat', dtype=np.float32, count=nx*ny)
data = data.reshape((nx,ny), order='F')
matplotlib.image.imsave('T.png', np.transpose(data), origin='lower', format='png')
OK, here is another possible solution (I separated it from my first cumbersome approach). It creates the plot immediately, less than a second. No renaming necessary or creation of a useless file.
I guess key is to use term png and ps 0.1.
I don't have a proof but I think ps 1 would be ca. 6 pixels large and would create some overlap and/or white pixels at the corner. Again, for whatever reason it seems to work with term png but not with term pngcairo.
What I tested (Win7, gnuplot 5.2.6) is a binary file having the pattern 00 00 FF repeated all over (I can't display null bytes here). Since gnuplot apparently reads 4 bytes per array item (format="%d"), this leads to an alternating RGB pattern if I am plotting with lc rgb var.
In the same way (hopefully) we can figure out how to read format="%f" and use it together with a color palette. I guess that's what you are looking for, right?
Further test results, comments, improvements and explanations are welcome.
Code:
### pixel image from matrix data without strange white border
reset session
SizeX = 400
SizeY = 200
set terminal png size SizeX,SizeY
set output "tbPixelImage.png"
set margins 0,0,0,0
unset colorbox
unset border
unset key
unset tics
set xrange[0:SizeX-1]
set yrange[0:SizeY-1]
plot "tbBinary.dat" binary array=(SizeX,SizeY) format="%d" w p pt 5 ps 0.1 lc rgb var
### end of code
Result:
My x and y labels are cut off the pic
I found the crop/nocrop option but didn't work.
How can I set a margin? and as you can see the titles (top right) are covered by the data. How can I set a margin there?
The following code comes from my bash script.
#set output
set terminal png large size 1920,1080 enhance background rgb '$BKGD_COLOR'
set output '$PLOT_OUTPUT_DIR/BW_${ArrayFile[$j]}_$DATE.png'
#set data
set datafile separator ","
set timefmt '%d/%m/%Y %H:%M:%S'
set xdata time
set format x "%d/%m/%Y\n%H:%M:%S"
#set axis (new style named 11, disable top and right axis, disable tics on top and right)
set style line 11 linecolor rgb '$TEXT_COLOR' linetype 1
set border 3 back linestyle 11
set tics nomirror font "/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf,16" textcolor rgb "$TEXT_COLOR"
#set grid
set style line 12 linecolor rgb '$TEXT_COLOR' linetype 0 linewidth 1
set grid back ls 12
#set line style
set style line 1 lc rgb '$RCVD_COLOR' pt 1 ps 1 lt 1 lw 2
set style line 2 lc rgb '$SENT_COLOR' pt 6 ps 1 lt 1 lw 2
#set text
set key font "/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf,10" textcolor rgb "$TEXT_COLOR"
set title 'Bandwidth (Mbps)' font "/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf,14" textcolor rgb '$TEXT_COLOR'
#Removed - set ylabel 'Mbps' textcolor rgb '$TEXT_COLOR'
set yrange [0:*]
#plot using the column 1 and 3 of the CSV file. with line points and title 'Bytes Received' and line style 1 (as defined above)
plot '$DIR/ResultsCSV/mg_bandwidth/${ArrayFile[$j]}.csv' u 1:3 w lp ls 1 t 'Bytes Received', '$DIR/ResultsCSV/mg_bandwidth/${ArrayFile[$j]}.csv' u 1:4 w lp ls 2 t 'Bytes Sent'
Set the font-size of your tics when setting the terminal. This size is used to determine the automatic margins:
set terminal png ... font ',16'
Alternatively you can set explicit margins with
set lmargin screen 0.05
set bmargin ...
For possible coordinate types, see e.g. https://stackoverflow.com/a/23180595/2604213
BTW: Use the pngcairo terminal which has a much better rendering quality.
#Christoph provided the answer about the margin, but you asked about your key as well.
In order to fix that, you can put the key in a different position. Doing
set key inside top left
will move the key to the left side, where the data won't cover it up. You can also move it ouside the plot altogether with
set key outside top right
which will move it to the right side and outside of the plot where it won't be covered up.
See help set key for more detail.
I would like to plot this datafile
"/root/temp.txt"
LB|30|421
CN|50|247
BR|20|370
SA|12|310
Where the first column is the X Axis, the second one is the Y Axis and the third one the label to put above each column of the histogram.
Before now, I use this syntax to plot the graph (but without any label)
set terminal png ;
set title "Hello" ;
set xlabel "Country" ;
set ylabel "values" ;
set style fill solid ;
set xtic rotate -45 ;
set datafile separator "|" ;
set style data histograms ;
plot '/root/temp.txt' using 2:xtic(1) notitle
But if I try to add labels gnu plot give me error!!
The syntax I use to add labels is
plot '/root/temp.txt' using 2:xtic(1):3 with labels notitle
Could you please help me?
Thank you
I am not sure that you can compress into one plotting format, but you can use replot (or replot-like plot):
plot '/root/temp.txt' using 2:xtic(1) notitle, '' u ($0):2:3 with labels notitle
Empty '' induce reuse the last input(file). Now, it equals with '/root/temp.txt'
To be nicer:
plot 'temp.txt' using 2:xtic(1) notitle, '' u ($0+0.1):($2+1):3 with labels notitle
Oh! And don't forget to set output of png term!