I have a blt real time graph in tcl/tk, the graph collects data over time with no time limit and it keeps storing all the data points into vectors and the user is able to scroll back and forth in the graph. The problem is that if I let the graph collect the points for a long period of time the cpu and memory consumption increases dramatically, i figure that for a 24 hour interval window should be fine. When i try to "unset x(0)" from the graph i get an error saying "command name invalid" i also try with "x delete 0" and same thing. Any help is much appreciated
This is how i initialize the graph:
proc startGraph {} {
global btnColor
global backColor
global startTime
global txtActionLevel
global txtAlertLevel
global x y1 y2 flagTime inStart inTime vectorFlag
global resolution
global notFirstTime
global txtActionLevel
set notFirstTime 0
set resolution 0
# Create stripchart widget
blt::stripchart .s -width 625 -height 330 -background $backColor -plotbackground black -font defaultFont
scrollbar .scroll -command { ScrollToEnd
.s axis view x } -orient horizontal -relief flat -background black -troughcolor $backColor -activebackground black -elementborderwidth 5
.s axis configure x -scrollcommand { .scroll set }
# Create BLT vectors
blt::vector create x
blt::vector create y1
blt::vector create y2
set startTime 0
set flagTime 0
set inStart -1
set inTime 0
set vectorFlag 0
.s configure -borderwidth 0 \
-leftmargin 0 \
-rightmargin 0 \
-plotborderwidth 0 \
-plotpadx {0 0} \
-plotpady {0 0}
.s legend configure -hide yes
.s grid configure -color gray \
-dashes 1 \
-minor 0 \
-hide 0
# X-axis
.s axis configure x -autorange 60 \
-shiftby 1 \
-stepsize 10 \
-subdivisions 1 \
-command FormatXLabel
# Alert txtAlertLevel
#.s tag create line -mapx 2 -mapy 2
proc FormatXLabel {widget x} {
set x [expr round($x)]
return [clock format $x -format "%I:%M:%S"]
}
# Y-axis
#.s axis configure y -title "C o u n t s"
image create photo .countsIcon -format PNG -file counts.png
label .titleGraph -image .countsIcon -background $backColor
place .titleGraph -in .measureView -x 0 -y 160
# Particles
.s element create Particles -symbol {} -color yellow -linewidth 1 \
-smooth linear -xdata x -ydata y1
# Bio
.s element create Bio -symbol {} -color red -linewidth 1 \
-smooth linear -xdata x -ydata y2
.s marker create line -name actionLine -coords {-Inf $txtActionLevel Inf $txtActionLevel} -linewidth 1 -outline orange
.s marker create line -name alertLine -coords {-Inf $txtAlertLevel Inf $txtAlertLevel} -linewidth 1 -outline green
place .s -in .measureView -x 10 -y 50
place .scroll -in .measureView -x 60 -y 380 -width 515 -height 35
#chartTime
}
this is where i add the values to the vectors:
set x(++end) [clock seconds]
set flagTime 0
set vectorFlag 1
set len [y1 length]
if {$len == 0} {
set startTime $x(end)
set y1(++end) $particle_sec
set y2(++end) $x_summary(bio_sec)
#if {$inStart < 0} {
# .s axis configure x -min "" -max ""
# set inStart 0
#}
} else {
set y1(++end) $particle_sec
set y2(++end) $x_summary(bio_sec)
}
puts "Vector length [x length]------"
puts "First value $x(0)----------"
#This is where i'm trying to catch whenever it reaches 60 seconds in this case
#when the length of the vector reaches 60 seconds it will unset the first value
#but it doesn't work it throws and invalid command name error
if {[x length] > 60} {
[unset -nocomplain x(0)]
}
#incr everyten
add_Result $particle_sec $bioSec [format "%.2f" $fv_eff]
for some odd reason when you use blt::vector create the "[unset -nocomplain x(0)]" doesn't seem to work, so i change it back to "x delete 0" without the square brackets and it works now.
When you put unset -nocomplain x(0) in [square brackets] and have it on its own like that, what you get is this:
It tries to unset that variable (I don't know if that will have problems; I don't use BLT vectors in my own code). There will be no errors from this. The result will be the empty string (it's documented).
It takes the result (an empty string) and uses it as a word of a command. The whole word, as you're not concatenating anything with it. The whole first word, in fact. You're going to try to evaluate the command whose name is the empty string, and pass it no other arguments. This is general Tcl semantics.
Now, creating a command with an empty name is legal, slightly tricky (because rename to an empty string deletes it — fully-qualified names are the workaround), and highly unusual. In your case, you've no such command and what you've really got is a bug. The bug? Those square brackets.
Use unset -nocomplain x(0) instead of [unset -nocomplain x(0)].
Note also that in many places online, when Tclers are putting Tcl code fragments inline in a place without fancy formatting, they'll put square brackets around the code. It's just a convention to make things easier to read. You shouldn't be seeing such things here on Stack Overflow.
Related
I have a netcdf file containing 4-D variables:
variables:
double maxvegetfrac(time_counter, veget, lat, lon) ;
maxvegetfrac:_FillValue = 1.00000002004088e+20 ;
maxvegetfrac:history = "From Topo.115MaCTRL_WAM_360_180" ;
maxvegetfrac:long_name = "Vegetation types" ;
maxvegetfrac:missing_value = 1.e+20f ;
maxvegetfrac:name = "maxvegetfrac" ;
maxvegetfrac:units = "-" ;
double mask_veget(time_counter, veget, lat, lon) ;
mask_veget:missing_value = -1.e+34 ;
mask_veget:_FillValue = -1.e+34 ;
mask_veget:long_name = "IF MYVEG4 EQ 10 AND I GE 610 AND J GT 286 THEN 16 ELSE MYVEG4" ;
mask_veget:history = "From desert_115Ma_3" ;
I'd like to use the variable "mask_veget" as a mask to alter values of the variable "maxvegetfrac" over specific regions, and over chosen values of its "veget" dimension.
To do so I am using ncap2. For example, if I want to set maxvegetfrac values over the 5th rank of veget dimension to 500 where mask_veget equals 6, I do :
> ncap2 -s "where (mask_veget(:,:,:,:)== 6) maxvegetfrac(:,5,:,:) = 500" test.nc
My problem is that in the resulting test.nc file, maxvegetfrac has been modified at the first rank of "veget" dimension, not the 5th one. And I get the same result if I run the script over the entire veget dimension:
ncap2 -s "where (mask_veget(:,:,:,:)== 6) maxvegetfrac(:,:,:,:) = 500" test.nc
So I am mistaking somewhere, but... where ?
Any help appreciated !
A couple of things you may not be aware of
you shouldn't be hyperslabbing a variable in the where body -it makes no sense at the moment.
It is ok to hyperslab in the where statement proving its a single index
as a dim with a single value collapses
Try this:
/*** hyper.nco *****/
maxvegetfrac5=maxvegetfrac(:,5,:,:);
where( mask_veget(:,5,:,:)== 6 )
maxvegetfrac5=500.0;
/* put the hyperslab back in */
maxvegetfrac(:,5,:,:)=maxvegetfrac5;
/* script end *****/
run the script now with the command
ncap2 -v -O -S hyper.nco test.nc out.nc
...Henry
i am plotting a tree in gnuplot as discussed here (How to plot tree/graph/web data on gnuplot?). However, i would like to include the edges weight of the tree, i.e. for each edge i have a number (e.g. 10, 20, 30, 40) that represents the edge weight. The figure below shows in red the edges weight that i want to plot in gnuplot (i added this using power point).
Can anyone tell me how to plot edges with weight in gnuplot?
I would propose a slight variation on the answer which you mention in your question. Let's assume that the coordinates of the vertices are stored in a file pnts.dat as follows:
0 5 10
1 20 20
2 15 15
3 30 30
4 40 10
Here, the first column records the corresponding label, while second and third columns contain the x- and y-coordinate, respectively.
The edges could be defined in a separate file edges.dat as:
0 1 30 0 1
1 2 40 0 -2
1 4 20 0 1
1 3 10 0 1
Here, the first two column contain the point indices (they refer to the first column of pnts.dat). The third column records the weight of a particular edge. Finally, the last two columns contain the x,y displacement of the generated associated label.
With this, the Gnuplot script could look like:
set xr [0:50]
set yr [0:50]
set size square
flePnts = 'pnts.dat'
fleEdges = 'edges.dat'
loadEdges = sprintf('< gawk '' \
FNR==NR{x[$1]=$2;y[$1]=$3;next;} \
{printf "%%f\t%%f\n%%f\t%%f\n\n", x[$1], y[$1], x[$2], y[$2];} \
'' %s %s', flePnts, fleEdges);
loadWeights = sprintf('< gawk '' \
FNR==NR{x[$1]=$2;y[$1]=$3;next;} \
{printf "%%f\t%%f\t%%s\n", (x[$1]+x[$2])/2 + $4, (y[$1]+y[$2])/2 + $5, $3} \
'' %s %s', flePnts, fleEdges);
plot \
loadEdges using 1:2 with lines lc rgb "black" lw 2 notitle, \
flePnts using 2:3:(0.6) with circles fill solid lc rgb "black" notitle, \
flePnts using 2:3:1 with labels tc rgb "white" font "Arial Bold" notitle, \
loadWeights using 1:2:3 with labels tc rgb "red" center font "Arial Bold" notitle
the loadEdges command invokes gawk in order to generate for all edges the corresponding pairs of x/y coordinates (delimited by a blank line)
loadWeights calculates for each edge the middle point and places a label at these coordinates (taking into account the required offset)
Finally, one obtains:
Addicionally, if you want to add arrows to the edges, you have to do these steps:
Add the arrow style:
set style arrow 1 head filled size screen 0.025,10,40 lc rgb "black" lw 2
Change the command with lines for with vectors
loadEdges using 1:2:3:4 with vectors arrowstyle 1 notitle, \
The following image show you the final result:
And this is the final code:
set xr [0:50]
set yr [0:50]
set size square
set style arrow 1 head filled size screen 0.025,10,40 lc rgb "black" lw 2
flePnts = 'pnts.dat'
fleEdges = 'edges.dat'
loadEdges = sprintf('< gawk '' \
FNR==NR{x[$1]=$2;y[$1]=$3;next;} \
{printf "%%f\t%%f\t%%f\t%%f\n\n", x[$1], y[$1], (x[$2]-x[$1]), (y[$2]-y[$1]);} \
'' %s %s', flePnts, fleEdges);
loadWeights = sprintf('< gawk '' \
FNR==NR{x[$1]=$2;y[$1]=$3;next;} \
{printf "%%f\t%%f\t%%s\n", (x[$1]+x[$2])/2 + $4, (y[$1]+y[$2])/2 + $5, $3} \
'' %s %s', flePnts, fleEdges);
plot \
loadEdges using 1:2:3:4 with vectors arrowstyle 1 notitle, \
flePnts using 2:3:(0.6) with circles fill solid lc rgb "black" notitle, \
flePnts using 2:3:1 with labels tc rgb "white" font "Arial Bold" notitle, \
loadWeights using 1:2:3 with labels tc rgb "red" center font "Arial Bold" notitle
Here is a suggestion which resembles #ewcz's solution, i.e. using a list of IDs with x,y coordinates and a list of connections using two IDs.
However, with the following differences:
all data in one file (in two different blocks addressed with index)
a gnuplot-only solution without awk or gawk and hence platform-independent. A lookup list is created with gnuplot, similar to this answer.
individual label offsets with rotation angle dA and relative length dR between the points (dR=0: at center of connector, dR=-1: at starting point,dR=1: at end point). Because of this, is recommended to use set size ratio -1 such that e.g. a rotation by 45 degrees is visually 45 degrees in the graph.
Data: SO42447683.dat
# ID x y
0 5 10
1 20 20
2 15 15
3 30 30
4 40 10
# ID1 ID2 weight dA dR
0 1 30 7 0
1 2 40 20 0
1 4 20 0 0
1 3 10 0 0
Script: (works for gnuplot>=5.0.0, without option textbox it works with >=4.6.0)
### plot tree with different labels
reset
FILE = "SO42447683.dat"
set size ratio -1
set key noautotitle
set offsets 0.5,0.5,0.5,0.5
set angle degrees
set style textbox opaque noborder
IdIdxs = XYs = ' '
stats FILE u (IdIdxs=IdIdxs.sprintf("%s:%d ",strcol(1),$0), \
XYs=XYs.sprintf("%g %g ",$2,$3)) index 0 nooutput
Px(i) = real(word(XYs,2*i+1))
Py(i) = real(word(XYs,2*i+2))
getIdx(col) = (c0=strstrt(IdIdxs,sprintf(" %s:",strcol(col))), \
c1=strstrt(IdIdxs[c0+1:],' ')+c0, \
s0=IdIdxs[c0:c1], c2=strstrt(s0,':'), int(s0[c2+1:]))
getLxy(colA,colR) = (idx0=getIdx(1), x0=Px(idx0),y0=Py(idx0), \
idx1=getIdx(2), x1=Px(idx1),y1=Py(idx1), \
rm = sqrt((x1-x0)**2+(y1-y0)**2)*(column(colR)+1)*0.5, \
a = atan2(y1-y0,x1-x0)+column(colA), \
Ly = y0+rm*sin(a), Lx=x0+rm*cos(a))
plot FILE index 1 u (idx0=getIdx(1),x0=Px(idx0)):(y0=Py(idx0)): \
(idx1=getIdx(2),Px(idx1)-x0):(Py(idx1)-y0) w vec lw 2 lc rgb "black" nohead, \
'' index 0 u 2:3 w p pt 7 ps 4 lc rgb "white",\
'' index 0 u 2:3 w p pt 6 ps 4 lc rgb "black",\
'' index 0 u 2:3:1 w labels, \
'' index 1 u (getLxy(4,5)):(Ly):3 w labels tc rgb "red" boxed
### end of script
Result:
i am trying to solve this bash script which reads an arithmetic expression from user and echoes it to the output screen with round up of 3 decimal places in the end.
sample input
5+50*3/20 + (19*2)/7
sample output
17.929
my code is
read x
echo "scale = 3; $x" | bc -l
when there is an input of
5+50*3/20 + (19*2)/7
**my output is **
17.928
which the machine wants it to be
17.929
and due to this i get the solution wrong. any idea ?
The key here is to be sure to use printf with the formatting spec of "%.3f" and printf will take care of doing the rounding as you wish, as long as "scale=4" for bc.
Here's a script that works:
echo -e "please enter math to calculate: \c"
read x
printf "%.3f\n" $(echo "scale=4;$x" | bc -l)
You can get an understanding of what is going on with the above solution, if you run this command at the commandline: echo "scale=4;5+50*3/20 + (19*2)/7" | bc the result will be 17.9285. When that result is provided to printf as an argument, the function takes into account the fourth decimal place and rounds up the value so that the formatted result displays with precisely three decimal places and with a value of 17.929.
Alternatively, this works, too without a pipe by redirecting the here document as input for bc, as follows which avoids creating a sub-shell:
echo -e "please enter math to calculate: \c"
read x
printf "%.3f\n" $(bc -l <<< "scale=4;$x")
You are not rounding the number, you are truncating it.
$ echo "5+50*3/20 + (19*2)/7" | bc -l
17.92857142857142857142
$ echo "scale = 3; 5+50*3/20 + (19*2)/7" | bc -l
17.928
The only way I know to round a number is using awk:
$ awk 'BEGIN { rounded = sprintf("%.3f", 5+50*3/20 + (19*2)/7); print rounded }'
17.929
So, in you example:
read x
awk 'BEGIN { rounded = sprintf("%.3f", $x; print rounded }'
I entirely agree with jherran that you are not rounding the number, you are truncating it. I would go on to say that scale is probably just not behaving at all the way you want it, possibly in a way that noone would want it to behave.
> x="5+50*3/20 + (19*2)/7"
> echo "$x" | bc -l
17.92857142857142857142
> echo "scale = 3; $x" | bc -l
17.928
Furthermore, because of the behaviour of scale, you are rounding each multiplication/division separately from the additions. Let me prove my point with some examples :
> echo "scale=0; 5/2" | bc -l
2
> echo "scale=0; 5/2 + 7/2" | bc -l
5
> echo "5/2 + 7/2" | bc -l
6.00000000000000000000
However scale without any operation doesn't work either. There is an ugly work-around :
> echo "scale=0; 5.5" | bc -l
5.5
> echo "scale=0; 5.5/1" | bc -l
5
So tow things come out of this.
If you want to use bc's scale, do it only for the final result already computed, and even then, beware.
Remember that rounding is the same as truncating a number + half of the desired precision.
Let us take the example of rounding to the nearest integer, if you add .5 to a number that should be rounded up, its integer part will take the next integer value and truncation will give the desired result. If that number should have been rounded down, then adding .5 will not change its integer value and truncation will yield the same result as when nothing was added.
Thus my solution follows :
> y=$(echo "$x" | bc -l)
> echo "scale=3; ($y+0.0005)/1" | bc -l # scale doesn't apply to the +, so we get the expected result
17.929
Again, note that the following doesn't work (as explained above), thus breaking it up in two operations is really needed :
> echo "scale=3; ($x+0.0005)/1" | bc -l
17.928
my data is quite simple, just a timestamp and a number like this:
2013-01-23 08:27:55 2801
2013-01-23 08:33:13 2801
2013-01-23 08:38:31 2660
2013-01-23 08:43:49 2785
2013-01-23 08:49:07 2785
I get the data from a another system. Space between date and time, and tabulator between "date time" and the number.
The whole time window is a few hours .. a couple of days. This overall time window contains one or more interesting parts (typically few..several hours), and I would like to mark or highlight these interesting parts somehow to make it easier and faster to analyse the graphs.
Th einteresting time window would need to be specified manually, e.g. from 2014-01-23 10:00:00 to 2013-01-24 12:00:00 for 2 hour window.
Drawing a rectancle to the background of the linegraph is one possibility, and changing the line color (or something similar) is another.
How can I do that from manually/separately defined time stamps? The best I was able to find was splitting the data with empty lines, and use something like this (from http://www.gnuplotting.org/introduction/plotting-data/):
set style line 1 lc rgb '#0060ad' lt 1 lw 2 pt 7 ps 1.5 # --- blue
set style line 2 lc rgb '#dd181f' lt 1 lw 2 pt 5 ps 1.5 # --- red
plot 'plotting-data3.dat' index 0 with linespoints ls 1, '' index 1 with linespoints ls 2
But splitting the data is a bit arduous (?); I'm looking for elegant gnuplot means to achive the goal. Robust splitting could be possible as well, if thats really the best alternative.
Below is the plotting function, works well but I'd like to mark/highlight selected parts of the graph.
-Paavo
function my_gnuplot {
if [ $# -ne 3 -o -z "$1" -o -z "$2" -o -z "$3" ]
then
echo $0 $FUNCNAME Invalid parameters, exiting
exit 3
fi
sid="$1" ; shift
stime="$1" ; shift
etime="$1" ; shift
if [ $sid -gt 0 -a $sid -lt 10 ]
then
title=0$sid
else
title=$sid
fi
gnuplot <<- EOF
reset
set terminal png size 1800,900 enhanced font myfont.ttf 10
set xdata time
set timefmt "%Y-%m-%d %H:%M:%S"
set format x "%Y-%m-%d %H:%M"
set title "SID=$title from $stime to $etime"
set grid
set xrange [ "$stime" : "$etime" ]
set yrange [ -2000 : 6000 ]
set style data linespoints
plot "sid_data_$sid.txt" using 1:9 notitle
EOF
}
You can use rectangles to fill the space pretty easily:
set xdata time
set timefmt '%Y-%m-%d %H:%M:%S'
set object 1 rectangle from "2013-01-23 08:33:13",graph 0 to "2013-01-23 08:43:49",graph 1 fs solid fc rgb "red" behind
plot 'test.dat' u 1:3 w lines ls -1
I have a large amount of data to plot, and I'm trying to use gnuplot. The data is a sorted array of around 80000 elements. By simply using
plot "myData.txt" using 1:2 with linespoints linetype 1 pointtype 1
I get the output, but: it takes time to render, and the points are often cluttered, with occasional gaps. To address the second, I thought of doing the bar chart: each of the entries
would correspond to a bar. However, I'm not sure how to achieve this. I would like to have some space between consecutive bars, but I don't expect that it would be visible. What would be your suggestion to plot the data?
........................
Due to large data volume, I guess it's best to group.
Note that my data looks like
1 11041.9
2 11041.9
3 9521.07
4 9521.07
5 9520.07
6 9519.07
7 9018.07
...
I would like to plot the data by a groups of 3, ie., the first vertical line should start at 9521.07 as a minimum of the points from 1, 2, 3, and end at 11041. The second vertical line should consider the following 3 points: 4, 5 and 6, and start at 9519.07 with an end at 9521.07, and so on.
Could this be achieved with gnuplot, given the data file as illustrated? If so, I would appreciate if someone posts a set of commands I should use.
To reduce the number of points gnuplot actually draws, you can use the every keyword, e.g.
plot "myData.txt" using 1:2 with linespoints linetype 1 pointtype 1 every 100
will plot every 100th data point.
I am not sure if it's possible to do what you want (plotting vertical lines) elegantly within gnuplot, but here is my solution (assuming a UNIX-y environment). First make an awk script called sort.awk:
BEGIN { RS = "" }
{
# the next two lines handle the case where
# there are not three lines in a record
xval = $1 + 1
ymin = ymax = $2
# find y minimum
if ($2 <= $4 && $2 <= $6)
ymin=$2
else if ($4 <= $2 && $4 <= $6 && $4 != "")
ymin=$4
else if ($6 <= $2 && $6 <= $4 && $6 != "")
ymin=$6
# find y maximum
if ($2 >= $4 && $2 >= $6)
ymax=$2
else if ($4 >= $2 && $4 >= $6)
ymax=$4
else if ($6 >= $2 && $6 >= $4)
ymax=$6
# print the formatted line
print ($1+1) " " ymin " " ymin " " ymax " " ymax
}
Now this gnuplot script will call it:
set terminal postscript enhanced color
set output 'plot.eps'
set boxwidth 3
set style fill solid
plot "<sed 'n;n;G;' myData.txt | awk -f sort.awk" with candlesticks title 'pretty data'
It's not pretty but it works. sed adds a blank line every 3 lines, and awk formats the output for the candlesticks style. You can also try embedding the awk script in the gnuplot script.
You can do something like that...(it'll be easiest on unix). You will need to insert a space every third line -- I don't see any way around that. If you're on unix, the command
awk 'NR % 3 == 0 {print ""} 1' myfile
should do it. ( see How do I insert a blank line every n lines using awk? )
Of course, you could (and probably should) pack that straight into your gnuplot file.
So, all said and done, you'd have something like this:
xval(x)=int(x)/3 #Return the x position on the plot
plot "< awk 'NR % 3 == 0 {print ""} 1' datafile" using (xval($1)):2 with lines