I want to plot results in gnuplot that all lie on a circle. For each value I have the corresponding x-y coordinates and a corresponding ID number (so 4 columns in total), but in an unsorted order. The rows are to be sorted in such a way that the direction of rotation is from -x => -y => +x => +y and end again at -x. So the center of the circle is at 0. How to implement this with "sort" (or alternatively with "awk"?) command (using Linux)?
At Theozh's suggestion, I will formulate my problem a little more precisely.
Imagine the dial of a clock with small dots as minute symbols. The position of the minute points can be determined by polar coordinates or by Cartesian coordinates. My file contains the Cartesian coordinates with an associated value (result). The file contains 60 lines with x - y coordinates and the result in the third column. Depending on the quadrant, the signs of x and y change, of course. Unfortunately, the lines are NOT sorted in such a way that they correspond to the sense of circulation. So the line for the point "minute 30" is not in line 30, but e.g. in line 17. The task is to sort the lines by the coordinates so that they appear in the order from 1 to 60. In the diagram, the x-axis would then simply be defined from 1 to 60 and the y-axis would then contain the results (from the 3rd column)
My unsorted file (coordinates for a unit circle, result values simplified for a better overview)
And this is what I want to have (8 rows (every 45°) sorted counterclockwise):
The angle can be calculated directly from the x/y coordinates using the atan2() function.
You do not say exactly what it is that you want to plot. If it is simply the points themselves (one point per line in the file), then this can be done easily inside gnuplot. I show the output from gnuplot 5.5. In earlier versions, and depending on exactly what you want to plot, it might require additional commands to sort the data in a separate step and then plot the sorted data. If you clarify what exactly is supposed to go into the plot, I will modify the example accordingly.
Example using 100 points with random x and y coordinates:
set print $RANDOM
do for [i=1:100] { print rand(0)-0.5, rand(0)-0.5 }
unset print
set xrange [-1:1]
set yrange [-1:1]
set angle degrees
set cbrange [-180:180]; set cblabel "Angle"
set style data linespoints
plot $RANDOM using 1:2:(atan2($2,$1)):(atan2($2,$1)) smooth zsort lc palette
Updated answer
Revised to show a plot of the sample data as given
$DATA << EOD
X Y Result
-0.707 -0.707 222
-0.707 0.707 888
0.707 -0.707 444
-1 0 111
1 0 555
0.707 0.707 666
0 1 777
0 -1 333
EOD
set datafile columnheader # allow for the line of labels
unset key
# atan2() returns a number between -pi and pi; convert this to 0 -> 1
# You could make it run from 0->60 if you know in advance
# there will be 60 evenly spaced points
xcoord(a) = (a + pi) / (2 * pi)
plot $DATA using (xcoord(atan2($2,$1))):3:(atan2($2,$1)) smooth zsort with points
And actually, if you don't care what order the points are drawn in, only where they end up, the command is even simpler because there is no need to sort!
plot $DATA using (xcoord(atan2($2,$1))):3 with points
Related
A datafile contains three columns with the first and second representing the x & y position of a circle at a time (in seconds) given by the third column. For example, the first two lines in "data.txt" give the position of two circles at time=0, followed by two blank lines then the position of the two circles at time=0.1 sec and so forth. The first few lines of data.txt are:
0 0 0
-1 -1 0
1 1.0 0.1
-1 -0.5 0.1
1.2 1.25 0.2
-0.5 -0.25 0.2
...
The Gnuplot code producing a series of frames (a movie) with the position of the two circles in time is:
set terminal gif size 1200,1200 animate delay 500
set output "movie.gif"
stats "data.txt" u 1:2 name "A"
set style circle radius graph 0.025; set style fill solid
set xrange [A_min_x*1.1:A_max_x*1.1]
set yrange [A_min_y*1.1:A_max_y*1.1]
do for [i=0:A_blocks-2] {
plot "data.txt" index i u 1:2 w circle
}
I'm trying to add a label or textbox of the form "Time=?" to each frame where the question mark is replaced by the number from the third column. Any suggestions on how to do it?
This answer requires the current version of gnuplot (5.4)
Assuming that all the column 3 entries in a given data block are the same, it should be sufficient to say
plot "data.txt" index i u 1:(FOO=strcol(3),column(2)) w circle title sprintf("Time = %s",FOO)
This updates the value of FOO for each line used. The title uses whatever the final update was for that plot. If you must specifically choose the column 3 value from a particular line within the block, or calculate something like an average, that's a harder problem. In that case please clarify.
Edit:
The reason this requires 5.4 is that earlier versions evaluated the title before reading the data for that plot. Version 5.4 evaluates the title after reading the data.
The first (not too obvious) solution which comes to my mind: plot your data in a loop and assign the value of the 3rd column to a variable, e.g. t. Use keyenty to print the legend with your time re-using the variable t. In order to avoid a symbol in the legend use plotting style with points and a point with pointsize 0.
Code:
### animation with time label
reset session
$Data <<EOD
0 0 0
-1 -1 0
1 1.0 0.1
-1 -0.5 0.1
1.2 1.25 0.2
-0.5 -0.25 0.2
EOD
set terminal gif size 400,400 animate delay 100
set output "SO70474478.gif"
stats $Data u 1:2 name "A" nooutput
set style circle radius graph 0.025; set style fill solid
set xrange [A_min_x*1.1:A_max_x*1.1]
set yrange [A_min_y*1.1:A_max_y*1.1]
set key top left
do for [i=0:A_blocks-1] {
plot $Data index i u 1:(t=$3,$2) w circle notitle, \
keyentry w points ps 0 ti sprintf("Time: %.1f",t)
}
set output
### end of code
Result:
I have a simple pcolor plot in Matlab (Version R 2016b) which I have uploaded as shown in the image below. I need to get only the blue sloped line which extends from the middle of the leftmost corner to the rightmost corner without hard-coding the matrix values.
For instance: One can see that the desired slope line has values somewhere approximately between 20 to 45 from the pcolor plot. (From a rough guess just by looking at the graph)
I'm applying the following code on the matrix named Slant which contains the plotted values.
load('Slant.mat');
Slant(Slant<20|Slant>50)=0;
pcolor(Slant); colormap(jet); shading interp; colorbar;
As one can see I hard-coded the values which I don't want to. Is there any method of detecting certain matrix values while making the rest equal to zero?
I used an other small algorithm of taking half the maximum value from the matrix and setting it to zero. But this doesn't work for other images.
[maxvalue, row] = max(Slant);
max_m=max(maxvalue);
Slant(Slant>max_m/2)=0;
pcolor(Slant); colormap(jet); shading interp; colorbar;
Here is another suggestion:
Remove all the background.
Assuming this "line" results in a Bimodal distribution of the data (after removing the zeros), find the lower mode.
Assuming the values of the line are always lower than the background, apply a logic mask that set to zeros all values above the minimum + 2nd_mode, as demonstrated in the figure below (in red circle):
Here is how it works:
A = Slant(any(Slant,2),:); % save in A only the nonzero data
Now we have A that looks like this:
[y,x] = findpeaks(histcounts(A)); % find all the mode in the histogram of A
sorted_x = sortrows([x.' y.'],-2); % sort them by their hight in decendet order
mA = A<min(A(:))+sorted_x(2,1); % mask all values above the second mode
result = A.*mA; % apply the mask on A
And we get the result:
The resulted line has some holes within it, so you might want to interpolate the whole line from the result. This can be done with simple math on the indices:
[y1,x1] = find(mA,1); % find the first nonzero row
[y2,x2] = find(mA,1,'last'); % find the last nonzero row
m = (y1-y2)/(x1-x2); % the line slope
n = y1-m*x1; % the intercept
f_line = #(x) m.*x+n; % the line function
So we get a line function f_line like this (in red below):
Now we want to make this line thicker, like the line in the data, so we take the mode of the thickness (by counting the values in each column, you might want to take max instead), and 'expand' the line by half of this factor to both sides:
thick = mode(sum(mA)); % mode thickness of the line
tmp = (1:thick)-ceil(thick/2); % helper vector for expanding
rows = bsxfun(#plus,tmp.',floor(f_line(1:size(A,2)))); % all the rows for each coloumn
rows(rows<1) = 1; % make sure to not get out of range
rows(rows>size(A,1)) = size(A,1); % make sure to not get out of range
inds = sub2ind(size(A),rows,repmat(1:size(A,2),thick,1)); % convert to linear indecies
mA(inds) = 1; % add the interpolation to the mask
result = A.*mA; % apply the mask on A
And now result looks like this:
Idea: Use the Hough transform:
First of all it is best to create a new matrix with only the rows and columns we are interested in.
In order to apply matlab's built in hough we have to create a binary image: As the line always has lower values than the rest, we could e.g. determine the lowest quartile of the brightnesses present in the picture (using quantile, and set these to white, everything else to black.
Then to find the line, we can use hough directly on that BW image.
My question about how to automate placing labels at the edge of a plot for data points that exceed the range of plot. This would be done for points that are outliers that, if plotted, skew the scale of the plot such that the plot is no longer useful, but still need to be noted.
For example, let's say I've got a data file called 'mydata.dat' that looks like this:
1 2
3 3
7 4
8 6
50 8
If I plot the data using
set yrange [0:10]
set xrange [0:10]
plot 'mydata.dat' w lp pt 1
then the last point (50,8) will not be plotted. To acknowledge, however, that there is a point outside the plotted range, a label and point would be placed at the edge (i.e., at 10,8) of the plot using
set label "" at 10,8 point pt 1
set label "50" at 9.75,8 right
Is there a way to automate this process? A way to have GNUplot read the entire data file and set points and labels at the edge of the plot?
To plot all point which are outside of the specified range, you'll need a second plot command using the labels plotting style.
In the using statement you can then check for the point being outside the range. If it is, you place a point with a corresponding label, otherwise you skip the point (using 1/0 as value):
set xrange [0:10]
set yrange [0:10]
set linetype 1 pt 7
plot 'mydata.dat' w lp lt 1, \
'' using ($1 > 10 ? 10 : 1/0):2:(sprintf('%d', $1)) with labels right offset -2,-0.25 point lt 1 notitle
Here, I check only for the x-value being larger than the specified maximum x-value. Maybe you need to adapt it in order to catch also outliers in y-direction or those smaller than 0.
After quite a bit of tweaking to an existing GNU plot, I managed to get the following:
reset
# png
set terminal png size 350,262 enhanced font 'Verdana,10'
# color definitions
set style line 1 lc rgb '#0060ad' lt 1 lw 2 pt 7 ps 2 # --- blue
unset key
set border 0
unset tics
# set view 342,0
set xrange [-300:300]
set yrange [-300:300]
n=0
do for [ii=1:99] {
n=n+1
set output sprintf('png/spiral%03.0f.png',n)
plot '1_1910.txt' every ::1::ii w l ls 1, \
'2_1910.txt' every ::1::ii w l ls 1
}
do for [ii=1:99] {
n=n+1
set output sprintf('png/spiral1%03.0f.png',n)
plot '1_1920.txt' every ::1::ii w l ls 1, \
'2_1920.txt' every ::1::ii w l ls 1
}
The idea is to create a set of PNG files, and then concatenate them all with animation.
I concatenate them all together using
convert -delay 2 -loop 0 png/*.png animation.gif
I want to make more tweaks:
I don't want to keep the line that follows the whole plot (even though animated). Instead, I want a "bullet point" (maybe the size of 5-10 pixels) with text moving next to it for each frame for each data point in the plot.
I want a text at the bottom right for the first set of frames (in the first 1:99 loop) and a different text replacing it, again in the bottom right corner (in the next 1:99 loop).
My code is based on this:
http://www.gnuplotting.org/animation-iv-trajectory/
but I can't figure out why his code has a "trail" that disappears, and why he has also a bullet point and I don't. As I said, I also want text that moves with the bullet point (same text for each data file).
I think you can do it with loops like this:
n = 0
do for [ii=0:60] {
n=n+1
set output sprintf('png/spiral%03.0f.png',n)
plot 'data.dat' every ::ii::ii w p ls 1, \
'data.dat' using 1:2:("foo") every ::ii::ii w labels offset 2
}
set label 1 "hyde" at 275,-275
do for [ii=60:0:-1] {
n=n+1
set output sprintf('png/spiral%03.0f.png',n)
plot 'data.dat' every ::ii::ii w p ls 1, \
'data.dat' using 1:2:("foo") every ::ii::ii w labels offset 2
}
Here is the result:
You can play with the offset coordinates to position the label text "foo" where you want.
To change the size of the point, change the value of ps in the line style command at the beginning of your script.
I am not sure what the purpose of the two different files is (1_1910.txt and 2_1920.txt), so I used the same file in my example. You may need to add to the plot command if you are plotting two different things at once.
As for your other questions, I think looking closely at the code in the example should give you an idea of what is going on. In the example, the range every ::1::ii plots points from 1 to ii, giving a line; the range every ::ii::ii plots just one point. Note that the abbreviations w l and w p in the example expand to with lines and with points, respectively.
In the example the line disappears due to the action of the second loop, which runs in reverse (for [ii=99:1:-1]). If you are copying that example, make sure also that your indices run over the correct values (you may have a number other than 99 of data points). Also note that indices in gnuplot start at 0, so if the first line of your data file contains data (not a header) you want to start your loop at ii=0 instead of ii=1.
I am having nearly the same problem here. I found a really nice hack! Instead of making multiple png's and then merge them, you just use set multiplot and then unset multiplot inside ONE loop. Being specific I used it this way:
do for [i=0:k]
{
set multiplot
plot function1
plot function2
unset multiplot
}
But unfortunately this won't work unless the data set you have is synchronized!, but yet can be solve this using if-statement!. Good Luck
Context
I'm creating a labyrinth generator (actually, more like a map generator) based on "chambers" want to connect to each other. I read these from text files and then convert to an internal format composed with LocatedNodes which are basically a node type and x-y coordinates. I regrouped them in NodeList where I put all functions to rotate/mirror/normalize these nodes.
A Map is a aggregation of chambers so it has a single NodeList containing these.
To sum up the hierarchy : Map <- NodeList <- LocatedNodes
Problem
To connect chambers, I compare the shapes of an opening of the 1st map and the shape of the area around the opening of the 2nd map. Let's begin with a example:
>>> print map6.nodes # nodes of the entire map
1 1
0 5 0 2
o⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
0 |#############
|#...........#
|#...........#
|#...........#
|#............
5 |#............
|#........
|#........
|#........
|#........
10 |#########
>>> print map6.openings() # just the nodes placed on the opening
1
8 2
o⎯⎯⎯⎯⎯
4 | .
|.....
|.
|.
|.
9 |.
>>> print map7.nodes # map we want to connect with the other
1 1
0 5 0 4
o⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
0 | ###########
| ..........#
| ..........#
|..............#
|..............#
5 |..............#
|..............#
7 |###############
>>> print map7.joinable_on() # area around the map7.openings()
-
1 3
o⎯⎯⎯⎯⎯
1 | .
|.....
|.
|.
|.
6 |.
>>> map7.joinable_on().normalized(x=0,y=0) == map6.openings().normalized(x=0, y=0)
True
It is not hard to compare map6.openings() et map7.joinable_on() because when the positions of the nodes are normalized, I can do a one-to-one comparison.
BUT, the hard part comes now:
I want to be able to compare these shapes independently of their position, rotation or mirroring.
What I tried
When searching for ideas, I found the pairing function (this function links two int to a unique int, so each coordinate x-y becomes a unique int). With that I could identify a shape uniquely by applying recursively this function on (x,y) coordinates. First problem, the unique int is really unique, so even with a 90° rotation the int changes, so I can't compare two shapes this way.
Question
Do you have an idea or a solution to help me get a unique id of a shape that doesn't change when this shape get mirrored, translated or rotated ?
There is a general scheme for creating ids that don't change when a shape is mirrored, translated, or rotated. Start with an id that does care about mirroring, translating, and rotating. When you get a shape, consider every possible mirroring, rotation, and translation, and compute an id for each case. This gives you a large number of ids, so just pick the numerically smallest one.
For the case of translation, another idea might be more practical - before (and/or after) doing all this, translate the shape so that its centre of gravity is at the origin, or as near to the origin as you can make it.
Seems like you should have your list of shapes with no translation, rotation, or mirroring. Then, to create a map you index a shape and provide a transformation matrix. So your input file would look something like:
Shapes
shape1
shape2
shape3
etc.
Those shapes all are built at the origin and have no translation.
Then, your map becomes:
Map
0 (index to shape1), transformation matrix (scaling, rotation, translation, mirroring)
2 (index to shape3), transformation matrix
0 (index to shape1), different transformation matrix
Then all you have to do to determine if two shapes in the map are the same is compare their indexes.