Getting columns values in TCL - tcl

I am trying to capture some data in TCL. I have below data :
{0.0 0.0} {0.741 0.48}
My required out put is 3rd column of this data.
0.741
how can i achieve it?
set oaDesign [ed]
set rprb [db::getShapes -of $oaDesign -lpp {INST_B drawing}]
set r [de::getBBox $rprb]
puts $r
{0.0 0.0} {0.741 0.48}
I just need 3rd column, which is 0.741

You would use lindex and lset, respectively, to access the nested list's elements at a known position:
% lindex $r 1 0
0.741
To write back into that list of lists, at a given position:
% lset r 1 0 0.0
{0.0 0.0} {0.0 0.48}
Did you search SO for previous answers, before raising your question?

Related

Getting number of duplicate elements in a list (in tcl)

I have a list which looks like
list = {ab bc 8 ab d1 10 xy uv bc ab xy 10 d1}
I would like to know how often each element of the list occurs inside it, that is, I need a result like this:
ab 3
bc 2
8 1
d1 2
....
I prefer a single line argument (if such exists) instead of a proc. I need to work with both: list elements and their frequency in the list.
Any advice is welcome.
Thank you!
Assuming that counter is the name of the dictionary where you want to collect this information (and is either currently unset or set to the empty string):
foreach item $list {dict incr counter $item}
You can then print that out in approximately the form you gave with:
dict for {item count} $counter {puts [format "%6s %-3d" $item $count]}
Note that this second line is about displaying the data, not actually finding it out.

Gnuplot one-liner to generate a titled line for each row in a CSV file

I've been trying to figure out gnuplot but haven't been getting anywhere for seemingly 2 reasons. My lack of understanding gnuplot set commands, and the layout of my data file. I've decided the best option is to ask for help.
Getting this gnuplot command into a one-liner is the hope.
Example rows from my CSV data file (MyData.csv):
> _TitleRow1_,15.21,15.21,...could be more, could be less
> _TitleRow2_,16.27,16.27,101,55.12,...could be more, could be less
> _TitleRow3_,16.19,16.19,20.8,...could be more, could be less
...(over 100 rows)
Contents of MyData.csv rows will always be a string as the first column for title, followed by an undetermined amount of decimal values. (Each row gets appended to periodically, so specifying an open ended amount of columns to include is needed)
What I'd like to happen is to generate a line graph showing a line for each row in the csv, using the first column as a row title, and the following numbers generating the actual line.
This is the I'm trying:
gnuplot -e 'set datafile separator ","; set key autotitle columnhead; plot "MyData.csv"'
Which results in:
set datafile separator ","; set key autotitle columnhead; plot "MyData.csv"
^
line 0: Bad data on line 2 of file MyData.csv
This looks like an amazing tool and I'm looking forward to learning more about it. Thanks in advance for any hints/assistance!
Your datafile format is very unfortunate for gnuplot which prefers data in columns.
Although, you can also plot rows (which is not straightforward in gnuplot, but see an example here). This requires a strict matrix, but the problem with your data is that you have a variable column count.
Actually, your CSV is not a "correct" CSV, because a CSV should have the same number of columns for all rows, i.e. if one row has less data than the row with maximum data the line should be filled with ,,, as many as needed. That's basically what the script below is doing.
With this you can plot rows with the option matrix (check help matrix). However, you will get some warnings warning: matrix contains missing or undefined values which you can ignore.
Alternatively, you could transpose your data (with variable column count maybe not straightforward). Maybe there are external tools which can do it easily. With gnuplot-only it will be a bit cumbersome (and first you would have to fill your shorter rows as in the example below).
Maybe there is a simpler and better gnuplot-only solution which I am currently not aware of.
Data: SO73099645.dat
_TitleRow1_, 1.2, 1.3
_TitleRow2_, 2.2, 2.3, 2.4, 2.5
_TitleRow3_, 3.2, 3.3, 3.4
Script:
### plotting rows with variable columns
reset session
FILE = "SO73099645.dat"
getColumns(s) = (sum [i=1:strlen(s)] (s[i:i] eq ',') ? 1 : 0) + 1
set datafile separator "\t"
colCount = 0
myNaNs = myHeaders = ''
stats FILE u (rowCount=$0+1, c=getColumns(strcol(1)), c>colCount ? colCount=c : 0) nooutput
do for [i=1:colCount] {myNaNs=myNaNs.',NaN' }
set table $Data
plot FILE u (s=strcol(1),c=getColumns(s),s.myNaNs[1:(colCount-c)*4]) w table
unset table
set datafile separator ","
stats FILE u (myHeaders=sprintf('%s "%s"',myHeaders,strcol(1))) nooutput
myHeader(n) = word(myHeaders,n)
set key noenhanced
plot for [row=0:rowCount-1] $Data matrix u 1:3 every ::1:row::row w lp pt 7 ti myHeader(row+1)
### end of script
As "one-liner":
FILE = "SO/SO73099645.dat"; getColumns(s) = (sum [i=1:strlen(s)] (s[i:i] eq ',') ? 1 : 0) + 1; set datafile separator "\t"; colCount = 0; myNaNs = myHeaders = ''; stats FILE u (rowCount=$0+1, c=getColumns(strcol(1)), c>colCount ? colCount=c : 0) nooutput; do for [i=1:colCount] {myNaNs=myNaNs.',NaN' }; set table $Data; plot FILE u (s=strcol(1),c=getColumns(s),s.myNaNs[1:(colCount-c)*4]) w table; unset table; set datafile separator ","; stats FILE u (myHeaders=sprintf('%s "%s"',myHeaders,strcol(1))) nooutput; myHeader(n) = word(myHeaders,n); set key noenhanced; plot for [row=0:rowCount-1] $Data matrix u 1:3 every ::1:row::row w lp pt 7 ti myHeader(row+1)
Result:

Can I skip inputting optional function parameters in tcl?

I know I can make tcl functions with optional input parameters. So if you don't enter a value for those parameters they can take on the default value that you give them. My question is when I call a function with optional parameters is it possible to skip the input of some and enter others? Like if there are 2x optional parameters how would I enter a value for the second parameter rather than the first?
I haven't really tried anything specifically because I'm not sure how to get around this problem.
proc area { height width {normalization 1.0} {scaling 1.0} {
....
}
[area 3 4 3.5]
Is there a way I could call area without changing the default value for normalization, while entering a value for scaling?
If I wanted a scaling value of 3.5 I can't really enter it in without also entering a value for normalization?
Yes, that's correct; if you want a scaling value of 3.5, you cannot enter it without also entering a value for normalization with the way the proc was written.
You can do something a bit more towards what you want to do with some tweaking (there's a whole wiki article discussing about ways to do this). A quick example with an array:
proc area {height width args} {
# Put the remaining parameters in an array, you might want to do validation checks before that
array set options $args
# If certain parameters are not provided, give default values
if {"-normalization" ni [array names options]} {
set options(-normalization) 1.0
}
if {"-scaling" ni [array names options]} {
set options(-scaling) 1.0
}
puts "Normalization: $options(-normalization)"
puts "Scaling: $options(-scaling)"
}
area 1 2 -normalization 3.5
# => Normalization: 3.5
# => Scaling: 1.0
area 1 2 -scaling 3.5
# => Normalization: 1.0
# => Scaling: 3.5
area 1 2
# => Normalization: 1.0
# => Scaling: 1.0
The pattern I use is this:
proc area {height width args} {
# The default values
set defaults {-normalization 1.0 -scaling 1.0}
# Merge with the actual arguments provided
set params [dict merge $defaults $args]
# Just extract the variables I care about using [dict update] with an empty body
dict update params -normalization norm -scaling scale {}
# Demonstrate what's been extracted
puts "Parameters: $height $width $norm $scale"
}
area 2 3 -scaling 17.3
This isn't perfect since it doesn't warn/error about unexpected arguments, provide a discovery mechanism, or handle argument name abbreviation, but it's simple and cheap and fairly fast. There have been proposals for changing that (I'm aware of two TIPs: 457, 479) but none have really gained traction so far; it's an area that is a tricky compromise between flexibility and performance.
(If you use the variable name as the actual parameter name and don't care at all about wrong arguments, you can just use dict with to do the extraction.)
A shorter version (using the same basic idea) might be:
proc area {height width args} {
array set {} [dict merge {-normalization 1.0 -scaling 1.0} $args]
puts "Parameters: $height $width $(-normalization) $(-scale)"
}
This uses the variable with the empty name as an array. Yes, that's legal (and relatively convenient) but I've never really warmed to it myself.
There have been proposals for changing that (I'm aware of two TIPs:
457, 479) but none have really gained traction so far.
If you are willing to use an established Tcl extension (https://next-scripting.org/), you can benefit from optional named parameters when implementing a proc:
package req nsf
nsf::proc area {height width {-normalization 1.0} {-scaling 1.0}} {
puts "arguments: $height $width $normalization $scaling"
}
area 1 2 -normalization 3.5
# arguments: 1 2 3.5 1.0
area 1 2 -scaling 3.5
# arguments: 1 2 1.0 3.5
area 1 2
# arguments: 1 2 1.0 1.0

How can I use TCL linear algebra package for setting the elemnt of a matrix

I am trying to use the ::math::linearalgebra:: package to do some simnple eigenvalue calculation for testing. The following code works and produces the desired result:
package require math
package require math::linearalgebra
set Mat [::math::linearalgebra::mkMatrix 8 8 0.0]
puts "a single row is: [::math::linearalgebra::getrow $Mat 0 ] "
However when I try to chnage an element of matrix Mat I get an error:
set Mat [::math::linearalgebra::mkMatrix 8 8 0.0]
::math::linearalgebra::setelem $Mat 0 1 1.0]
puts "a single row is: [::math::linearalgebra::getrow $Mat 0 ] "
The error is:
can't read "mat": no such variable
while executing "lset mat $row $col $newvalue"
(procedure "::math::linearalgebra::setelem" line 4)
How do I modify the elements of the created matrix if not with ::setelem?
Thanks
Per the manual, you have to give the name of the matrix. Thus you should do:
set Mat [::math::linearalgebra::mkMatrix 8 8 0.0]
::math::linearalgebra::setelem Mat 0 1 1.0

Loading column from CSV file as a list assigned to a variable

given is a function f(a,b,x,y) in gnuplot, where we got a 3D-space with x,y,z (using splot).
Also given is a csv file (without any header) of the following structure:
2 4
1 9
6 7
...
Is there a way to read out all the values of the first column and assign them to the variable a? Implicitly it should create something like:
a = [2,1,6]
b = [4,9,7]
The idea is to plot the function f(a,b,x,y) having iterated for all a,b tuples.
I've read through other posts where I hoped it would be related to it such as e.g. Reading dataset value into a gnuplot variable (start of X series). However I could not make any progres.
Is there a way to go through all rows of a csv file with two columns, using the each column value of a row as the parameter of a function?
Say you have the following data file called data:
1 4
2 5
3 6
You can load the 1st and 2nd column values to variables a and b easily using an awk system call (you can also do this using plot preprocessing with gnuplot but it's more complicated that way):
a=system("awk '{print $1}' data")
b=system("awk '{print $2}' data")
f(a,b,x,y)=a*x+b*y # Example function
set yrange [-1:1]
set xrange [-1:1]
splot for [i in a] for [j in b] f(i,j,x,y)
This is a gnuplot-only solution without the need for a system call:
a=""
b=""
splot "data" u (a=sprintf(" %s %f", a, $1), b=sprintf(" %s %f", b, \
$2)):(1/0):(1/0) not, for [i in a] for [j in b] f(i,j,x,y)