Customize color ranges in Gnuplot - gnuplot

I have a contour plot running just fine and it is generating an equal amount of color for different values.
What I want is to generate the label on the right side to have a right color for each block.
current result:
What I want is to have this value on each block:
---- 300
---- 100
---- 70
---- 30
---- 10
---- 1
---- 0
Edit:
When I add this code:
set cbtics ('300' 300, '100' 100, '30' 30, '10' 10, '1' 1, '0.5' 0.5, '0.1' 0.1, '0.01' 0.01, '0' 0);
set palette defined (0.1 "#4CAF4F",0.3 "#65B443",0.5 "#7FBA38",0.7 "#98BF2D",0.9 "#B2C521",1.0 "#CBCA16",2 "#E5D00B",3 "#FFD600",4 "#FFC400",5 "#FFB300",6 "#FFA100",7 "#FF9000",8 "#FF7E00",10 "#FF6D00",30 "#F85A00",50 "#F14800",70 "#EA3600",90 "#E32400",100 "#DC1200",300 "#D50000")
The result is a uneven:
I want the ticks to be even but could not make it.

I would recommend that you rescale your output from the scale 0 to 300 to a scale in which the values vary linearly between the manually defined labels on the color bar:
rescale(x) = ( x >= 0. && x < 1. ? x : \
x >= 1. && x < 10. ? 1.+(x-1.)/(10.-1.) : \
x >= 10. && x < 30. ? 2.+(x-10.)/(30.-10.) : \
x >= 30. && x < 70. ? 3.+(x-30.)/(70.-30.) : \
x >= 70. && x < 100. ? 4.+(x-70.)/(100.-70.) : \
x >= 100. && x < 300. ? 5.+(x-100.)/(300.-100.) : \
1/0)
set cbrange [0:6]
set cbtics ("0" 0, "1" 1, "10" 2, "30" 3, "70" 4, "100" 5, "300" 6)
set pm3d
splot rescale(x**2+y**2)
If you want to limit the colors to one per block, add the following line:
set palette maxcolors 6
Finally, note that if you have a map the labeling is sufficient as is. If you have a 3D plot, you might want to also relabel the z axis:
set ztics ("0" 0, "1" 1, "10" 2, "30" 3, "70" 4, "100" 5, "300" 6)

Related

symmetric logarithmic plot (symlog)

How would you make a symlog plot in gnuplot?
matplolib has such a feature:
I'm thinking about some axis + tics manipulation or multiplot where on panel has reversed axis.
Finally, I need this for the z axis (cbrange) the show the residuals as an image.
symlog(x) = (-1 < x && x < 1) ? x/10. \
: (x < 0) ? -log(-x) - 0.1 \
: log(x) + 0.1
invsymlog(x) = (-0.1 < x && x < 0.1) ? x*10. \
: (x < 0) ? -exp(-(x+0.1)) \
: exp(x-0.1)
set xlabel "Symlog axis"
set nonlinear x via symlog(x) inv invsymlog(x)
set xrange [-4*pi : 4*pi]
set sample 500
set xtics -10,1,10 nomirror
set xtics add ("//" 0)
set xtics font ",10"
plot x*cos(x)
The region between -1 and +1 is linear, the rest is log scale.

Incrementing variable increments before draw function runs

I am a new programmer using code sculptor with python 3. I have an animation project and one of the requirements is to change the background between two different settings 100 times
At the bottom, I've added print(i) to show what the problem with my code is. Basically, the "i" variable gets incremented, but it doesn't run the draw function, so the variable is incremented without the background changing. I've tried changing the positions of the draw function and nothing seems to be working. I am wondering if someone on this site could give me pointers on what to do in this situation. My overall goal is to run the program and have the background change as the train moves. You can see the program running by changing the values in the for loops to low values such as 1 and 1.
<pre>import simplegui
import time
def draw_handler(canvas):
global x
global flag
x = x+3
flag = flag + 2
if flag >= 320:
x = x-930
flag = -300
canvas.draw_circle([550,50], 40, 4, "yellow", "yellow")
points = [[0,500], [0,600], [600,600], [600,500]]
canvas.draw_polygon(points, 1, "black", "sienna")
points2 = [[155+x,325], [155+x,450], [455+x,450], [455+x,325]]
canvas.draw_polygon(points2, 1, "black", "deepskyblue")
points3 = [[155+x,250],[155+x,325],[235+x,325],[235+x,250]]
canvas.draw_polygon(points3, 1, "black", "deepskyblue")
canvas.draw_circle([190+x,470], 35, 4, "dimgrey","lightgray")
canvas.draw_circle([267+x,470], 35, 4, "dimgrey","lightgray")
canvas.draw_circle([344+x,470], 35, 4, "dimgrey","lightgray")
canvas.draw_circle([421+x,470], 35, 4, "dimgrey","lightgray")
points4 = [[120+x,225],[120+x,255],[265+x,255],[265+x,225]]
canvas.draw_polygon(points4, 1, "black", "black")
points5 = [[425+x,235], [425+x,325], [445+x,325], [445+x,235]]
canvas.draw_polygon(points5, 1, "black", "black")
canvas.draw_line([410+x,230], [460+x, 230], 7, "black")
canvas.draw_line([175+x,350], [427+x, 350], 2, "red")
canvas.draw_line([175+x,350], [175+x, 420], 2, "red")
canvas.draw_line([427+x,350], [427+x, 420], 2, "red")
canvas.draw_line([175+x,420], [427+x, 420], 2, "red")
global color
color = ""
global y
global i
i = 0
for i in range(100):
for i in range(20000):
i = i + 10
print(i)
if i <= 10000:
color = "aliceblue"
elif i > 10000:
color = "red"
elif i > 20000:
i = 0
frame = simplegui.create_frame('Testing', 600, 600)
frame.set_draw_handler(draw_handler)
frame.set_canvas_background(color)
x = 0
flag = 0
frame.start()</pre>
edit: Here is a direct link to the code in CodeSkulptor - https://py3.codeskulptor.org/#user302_ZF0cMFUoOAK9PvH.py

Generation of pie chart using gnuplot

I have a CSV file with data like:
name,age
raju,23
anju,34
manju,56
sanju,56
I'm trying to generate a pie chart using gnuplot.
Here's the command I'm executing:
#!/usr/bin/gnuplot -persist
reset
set terminal wxt
unset key
set datafile separator ","
set xlabel "USERS"
set ylabel "AGE"
plot 'file.csv' using ($0):2:($0):xticlabels(1) with circles lc variable notitle
What am I doing wrong?
Apparently, Gnuplot does not support pie charts yet; but we can draw it by hand.
First, we need to obtain the angles and percentages related to the second column in your datafile:
set datafile separator ','
stats 'file.csv' u 2 noout # get STATS_sum (sum of column 2)
ang(x)=x*360.0/STATS_sum # get angle (grades)
perc(x)=x*100.0/STATS_sum # get percentage
Then configure the canvas:
set size square # square canvas
set xrange [-1:1.5]
set yrange [-1.25:1.25]
set style fill solid 1
unset border
unset tics
unset key
and draw the pie chart:
Ai = 0.0; Bi = 0.0; # init angle
mid = 0.0; # mid angle
i = 0; j = 0; # color
yi = 0.0; yi2 = 0.0; # label position
plot 'file.csv' u (0):(0):(1):(Ai):(Ai=Ai+ang($2)):(i=i+1) with circle linecolor var,\
'file.csv' u (1.5):(yi=yi+0.5/STATS_records):($1) w labels,\
'file.csv' u (1.3):(yi2=yi2+0.5/STATS_records):(j=j+1) w p pt 5 ps 2 linecolor var,\
'file.csv' u (mid=Bi+ang($2)*pi/360.0, Bi=2.0*mid-Bi, 0.5*cos(mid)):(0.5*sin(mid)):(sprintf('%.0f (%.1f\%)', $2, perc($2))) w labels
The first line in the plot command draws the pie chart, where the columns (0):(0):(1):(Ai):(Ai=Ai+ang($2)):(i=i+1) are:
columns 1-2: x and y coordinates of the center of the disk
column 3: radius of the disk
column 4-5: begin and end angles of the region
column 6: color of the region
The second and third lines in the plot command place the labels, and the last line puts the percentages in the middle of each region.
The result:
References: (1) Gnuplot surprising (2) Gnuplot tricks
EDIT:
Based on two related questions (this and this), a new script is proposed:
filename = 'test.csv'
rowi = 1
rowf = 7
# obtain sum(column(2)) from rows `rowi` to `rowf`
set datafile separator ','
stats filename u 2 every ::rowi::rowf noout prefix "A"
# rowf should not be greater than length of file
rowf = (rowf-rowi > A_records - 1 ? A_records + rowi - 1 : rowf)
angle(x)=x*360/A_sum
percentage(x)=x*100/A_sum
# circumference dimensions for pie-chart
centerX=0
centerY=0
radius=1
# label positions
yposmin = 0.0
yposmax = 0.95*radius
xpos = 1.5*radius
ypos(i) = yposmax - i*(yposmax-yposmin)/(1.0*rowf-rowi)
#-------------------------------------------------------------------
# now we can configure the canvas
set style fill solid 1 # filled pie-chart
unset key # no automatic labels
unset tics # remove tics
unset border # remove borders; if some label is missing, comment to see what is happening
set size ratio -1 # equal scale length
set xrange [-radius:2*radius] # [-1:2] leaves space for labels
set yrange [-radius:radius] # [-1:1]
#-------------------------------------------------------------------
pos = 0 # init angle
colour = 0 # init colour
# 1st line: plot pie-chart
# 2nd line: draw colored boxes at (xpos):(ypos)
# 3rd line: place labels at (xpos+offset):(ypos)
plot filename u (centerX):(centerY):(radius):(pos):(pos=pos+angle($2)):(colour=colour+1) every ::rowi::rowf w circle lc var,\
for [i=0:rowf-rowi] '+' u (xpos):(ypos(i)) w p pt 5 ps 4 lc i+1,\
for [i=0:rowf-rowi] filename u (xpos):(ypos(i)):(sprintf('%05.2f%% %s', percentage($2), stringcolumn(1))) every ::i+rowi::i+rowi w labels left offset 3,0
This new code results in:

How to select Y values at X position in Groovy?

this is sort of a mathy question...
I had a question prior to this about normalizing monthly data here :
How to produce X values of a stretched graph?
I got a good answer and it works well, the only issue is that now I need to check X values of one month with 31 days against X values of a month with 28.
So my question would be: If I have two sets of parameters like so:
x | y x2 | y2
1 | 10 1.0 | 10
2 | 9 1.81 | 9.2
3 | 8 2.63 | 8.6
4 | 7 3.45 | 7.8
5 | 6 4.27 | 7
6 | 5 5.09 | 6.2
7 | 4 5.91 | 5.4
8 | 3 6.73 | 4.2
9 | 2 7.55 | 3.4
10 | 1 8.36 | 2.6
9.18 | 1.8
10.0 | 1.0
As you can see, the general trend is the same for these two data sets.
However, if I run these values through a cross-correlation function (the general goal), I will get something back that does not reflect this, since the data sets are of two different sizes.
The real world example of this would be, say, if you are tracking how many miles you run per day:
In February (with 28 days), during the first week, you run one mile each day. During the second week, you run two miles each day, etc.
In March (with 31 days), you do the same thing, but run for one mile for eight days, two miles for eight days, three miles for eight days, and four miles for seven days.
The correlation coefficient according to the following function should be almost exactly 1:
class CrossCorrelator {
def variance = { x->
def v = 0
x.each{ v += it**2}
v/(x.size()) - (mean(x)**2)
}
def covariance = {x, y->
def z = 0
[x, y].transpose().each{ z += it[0] * it[1] }
(z / (x.size())) - (mean(x) * mean(y))
}
def coefficient = {x, y->
covariance(x,y) / (Math.sqrt(variance(x) * variance(y)))
}
}
def i = new CrossCorrelator()
i.coefficient(y values, y2 values)
Just looking at the data sets, it seems like the graphs would be exactly the same if I were to grab the values at 1, 2, 3, 4, 5, 6, 7, 8, 9, and 10, and the function would produce a more accurate result.
However, it's skewed since the lengths are not the same.
Is there some way to locate what the values at the integers in the twelve-value data set would be? I haven't found a simple way to do it, but this would be incredibly helpful.
Thanks in advance,
5
Edit: As per request, here is the code that generates the X values of the graphs:
def x = (1..12)
def y = 10
change = {l, size ->
v = [1]
l.each{
v << ((((size-1)/(x.size() - 1)) * it) + 1)
}
v -= v.last()
return v
}
change(x, y)
Edit: Not working code as per another request:
def normalize( xylist, days ) {
xylist.collect { x, y -> [ x * ( days / xylist.size() ), y ] }
}
def change = {l, size ->
def v = [1]
l.each{
v << ((((size-1)/(l.size() - 1)) * it) + 1)
}
v -= v.last()
return v
}
def resample( list, min, max ) {
// We want a graph with integer points from min to max on the x axis
(min..max).collect { i ->
// find the values above and below this point
bounds = list.inject( [ a:null, b:null ] ) { r, p ->
// if the value is less than i, set it in r.a
if( p[ 0 ] < i )
r.a = p
// if it's bigger (and we don't already have a bigger point)
// then set it into r.b
if( !r.b && p[ 0 ] >= i )
r.b = p
r
}
// so now, bounds.a is the point below our required point, and bounds.b
// Deal with the first case (where a is null, because we are at the start)
if( !bounds.a )
[ i, list[ 0 ][ 1 ] ]
else {
// so work out the distance from bounds.a to bounds.b
dist = ( bounds.b[0] - bounds.a[0] )
// And how far the point i is along this line
r = ( i - bounds.a[0] ) / dist
// and recalculate the y figure for this point
y = ( ( bounds.b[1] - bounds.a[1] ) * r ) + bounds.a[1]
[ i, y ]
}
}
}
def feb = [9, 3, 7, 23, 15, 16, 17, 18, 19, 13, 14, 8, 13, 12, 15, 6, 7, 13, 19, 12, 7, 3, 4, 15, 6, 17, 8, 19]
def march = [8, 12, 4, 17, 11, 15, 12, 8, 9, 13, 12, 7, 3, 4, 8, 2, 17, 19, 21, 12, 12, 13, 14, 15, 16, 7, 8, 19, 21, 14, 16]
//X and Y Values for February
z = [(1..28), change(feb, 28)].transpose()
//X and Y Values for March stretched to 28 entries
o = [(1..31), change(march, 28)].transpose()
o1 = normalize(o, 28)
resample(o1, 1, 28)
If I switch "march" in the o variable declaration to (1..31), the script runs successfully. When I try to use "march," I get "
java.lang.NullPointerException: Cannot invoke method getAt() on null object"
Also: I try not to directly copy code just because it's bad practice, so one of the functions I changed basically does the same thing, it's just my version. I'll get around to refactoring the rest of it eventually, too. But that's why it's slightly different.
Ok...here we go...this may not be the cleanest bit of code ever...
Let's first generate two distributions, both from 1 to 10 (in the y axis)
def generate( range, max ) {
range.collect { i ->
[ i, max * ( i / ( range.to - range.from + 1 ) ) ]
}
}
// A distribution 10 elements long from 1 to 10
def e1 = generate( 1..10, 10 )
// A distribution 14 elements long from 1 to 10
def e2 = generate( 1..14, 10 )
So now, e1 and e2 are:
[1.00,1.00], [2.00,2.00], [3.00,3.00], [4.00,4.00], [5.00,5.00], [6.00,6.00], [7.00,7.00], [8.00,8.00], [9.00,9.00], [10.00,10.00]
[1.00,0.71], [2.00,1.43], [3.00,2.14], [4.00,2.86], [5.00,3.57], [6.00,4.29], [7.00,5.00], [8.00,5.71], [9.00,6.43], [10.00,7.14], [11.00,7.86], [12.00,8.57], [13.00,9.29], [14.00,10.00]
respectively (to 2dp). Now, using the code from the previous question, we can normalize these to the same x range:
def normalize( xylist, days ) {
xylist.collect { x, y -> [ x * ( days / xylist.size() ), y ] }
}
n1 = normalize( e1, 10 )
n2 = normalize( e2, 10 )
This means n1 and n2 are:
[1.00,1.00], [2.00,2.00], [3.00,3.00], [4.00,4.00], [5.00,5.00], [6.00,6.00], [7.00,7.00], [8.00,8.00], [9.00,9.00], [10.00,10.00]
[0.71,0.71], [1.43,1.43], [2.14,2.14], [2.86,2.86], [3.57,3.57], [4.29,4.29], [5.00,5.00], [5.71,5.71], [6.43,6.43], [7.14,7.14], [7.86,7.86], [8.57,8.57], [9.29,9.29], [10.00,10.00]
But, as you correctly state they have different numbers of sample points, so cannot be compared easily.
But we can write a method to step through each point we want in our graph, fond the two closest points, and interpolate a y value from the values of these two points like so:
def resample( list, min, max ) {
// We want a graph with integer points from min to max on the x axis
(min..max).collect { i ->
// find the values above and below this point
bounds = list.inject( [ a:null, b:null ] ) { r, p ->
// if the value is less than i, set it in r.a
if( p[ 0 ] < i )
r.a = p
// if it's bigger (and we don't already have a bigger point)
// then set it into r.b
if( !r.b && p[ 0 ] >= i )
r.b = p
r
}
// so now, bounds.a is the point below our required point, and bounds.b
if( !bounds.a ) // no lower bound...take the first element
[ i, list[ 0 ][ 1 ] ]
else if( !bounds.b ) // no upper bound... take the last element
[ i, list[ -1 ][ 1 ] ]
else {
// so work out the distance from bounds.a to bounds.b
dist = ( bounds.b[0] - bounds.a[0] )
// And how far the point i is along this line
r = ( i - bounds.a[0] ) / dist
// and recalculate the y figure for this point
y = ( ( bounds.b[1] - bounds.a[1] ) * r ) + bounds.a[1]
[ i, y ]
}
}
}
final1 = resample( n1, 1, 10 )
final2 = resample( n2, 1, 10 )
now, the values final1 and final2 are:
[1.00,1.00], [2.00,2.00], [3.00,3.00], [4.00,4.00], [5.00,5.00], [6.00,6.00], [7.00,7.00], [8.00,8.00], [9.00,9.00], [10.00,10.00]
[1.00,1.00], [2.00,2.00], [3.00,3.00], [4.00,4.00], [5.00,5.00], [6.00,6.00], [7.00,7.00], [8.00,8.00], [9.00,9.00], [10.00,10.00]
(obviously, there is some rounding here, so 2d.p. is hiding the fact that they are not exactly the same)
Phew... Must be home-time after that ;-)
EDIT
As pointed out in the edit to the question, there was a bug in my resample method that caused it to fail in certain conditions...
I believe this has now been fixed in the code above, and from the given example:
def march = [8, 12, 4, 17, 11, 15, 12, 8, 9, 13, 12, 7, 3, 4, 8, 2, 17, 19, 21, 12, 12, 13, 14, 15, 16, 7, 8, 19, 21, 14, 16]
o = [ (1..31), march ].transpose()
// X values squeezed to be between 1 and 28 (instead of 1 to 31)
o1 = normalize(o, 28)
// Then, resample this graph so there are only 28 points
v = resample(o1, 1, 28)
If you plot the original 31 points (in o) and the new graph of 28 points (in v), you get:
Which doesn't look too bad.
I have no idea what the change method was supposed to do, so I have omitted it from this code

Selecting colors that are furthest apart

I'm working on a project that requires me to select "unique" colors for each item. At times there could be upwards of 400 items. Is there some way out there of selecting the 400 colors that differ the most? Is it as simple as just changing the RGB values by a fixed increment?
You could come up with an equal distribution of 400 colours by incrementing red, green and blue in turn by 34.
That is:
You know you have three colour channels: red, green and blue
You need 400 distinct combinations of R, G and B
So on each channel the number of increments you need is the cube root of 400, i.e. about 7.36
To span the range 0..255 with 7.36 increments, each increment must be about 255/7.36, i.e. about 34
Probably HSL or HSV would be a better representations than RGB for this task.
You may find that changing the hue gives better variability perception to the eye, so adjust your increments in a way that for every X units changed in S and L you change Y (with Y < X) units of hue, and adjust X and Y so you cover the spectrum with your desired amount of samples.
Here is my final code. Hopefully it helps someone down the road.
from PIL import Image, ImageDraw
import math, colorsys, os.path
# number of color circles needed
qty = 400
# the lowest value (V in HSV) can go
vmin = 30
# calculate how much to increment value by
vrange = 100 - vmin
if (qty >= 72):
vdiff = math.floor(vrange / (qty / 72))
else:
vdiff = 0
# set options
sizes = [16, 24, 32]
border_color = '000000'
border_size = 3
# initialize variables
hval = 0
sval = 50
vval = vmin
count = 0
while count < qty:
im = Image.new('RGBA', (100, 100), (0, 0, 0, 0))
draw = ImageDraw.Draw(im)
draw.ellipse((5, 5, 95, 95), fill='#'+border_color)
r, g, b = colorsys.hsv_to_rgb(hval/360.0, sval/100.0, vval/100.0)
r = int(r*255)
g = int(g*255)
b = int(b*255)
draw.ellipse((5+border_size, 5+border_size, 95-border_size, 95-border_size), fill=(r, g, b))
del draw
hexval = '%02x%02x%02x' % (r, g, b)
for size in sizes:
result = im.resize((size, size), Image.ANTIALIAS)
result.save(str(qty)+'/'+hexval+'_'+str(size)+'.png', 'PNG')
if hval + 10 < 360:
hval += 10
else:
if sval == 50:
hval = 0
sval = 100
else:
hval = 0
sval = 50
vval += vdiff
count += 1
Hey I came across this problem a few times in my projects where I wanted to display, say, clusters of points. I found that the best way to go was to use the colormaps from matplotlib (https://matplotlib.org/stable/tutorials/colors/colormaps.html) and
colors = plt.get_cmap("hsv")[np.linspace(0, 1, n_colors)]
this will output rgba colors so you can get the rgb with just
rgb = colors[:,:3]

Resources