Related
I'am using Octave 5.2.0. to create a double axed plot with plotyy (in a subplot)
I created xticklabels with text() function, giving me an object with ypositions and xtick (rotated).
My problem is that when I move the curves of the subplot 1 (e.g. down) using the 'pan', the xticklabels move also in the same direction until they are hidden after the second plot and I have to move the curve in the opposite direction to see them again.
Second question. Is there no xticklabelrotation possible without using the text() function because this is very slow (5 min for 4000 dates). I include a part of the code. and 2 pictures (1 after moving the curve UP). Thanks for helping.
function plotKoersIndicSub(varargin)
[labelsVrX,labelsVrXNrs,xDateEpoch]=prepXVrPlot(varargin(dateKol)); [dateKol,P1Y1Kol,P1Y2Kol,P2Y1Kol,P2Y2Kol,labP1Y1,labP1Y2,labP2Y1,labP2Y2]=splitVarargin(varargin,nargin);
clf
hax1 =subplot (2, 1, 1);
[ax,hCurve1,hCurve2] = plotyy (xDateEpoch,varargin(P1Y1Kol(1)){:},xDateEpoch,varargin(P1Y2Kol(1)){:}, #plot, #plot);
fig=gcf();
set(fig,'units','centimeters'); set(fig,'position',[19,2.8,15,15]);
set(hCurve1, "linestyle", ":"); set(hCurve1,'LineWidth',2); set(hCurve1,'Color','blue');
set(hCurve2, "linestyle", "-"); set(hCurve2,'LineWidth',2); set(hCurve2,'Color','red');
hYlab1=ylabel(ax(1), labP1Y1); set (hYlab1, "fontsize", 14);
hYlab2=ylabel(ax(2), labP1Y2); set (hYlab2, "fontsize", 14);
k1Mx=max(varargin(P1Y1Kol(1)){:});k2Mx=max(varargin(P1Y2Kol(1)){:});
k1Min=min(varargin(P1Y1Kol(1)){:});k2Min=min(varargin(P1Y2Kol(1)){:});
ylim(ax(1),[k1Min k1Mx]);ylim(ax(2),[k2Min k2Mx]);
linkaxes(ax,'x');
set(gca,'xticklabel',labelsVrXNrs);
h = get(gca,'xlabel');
xlabelposition = get(h,'position');
yposition = xlabelposition(2); % yposition = 0.80090
yposition = repmat(yposition,columns(xDateEpoch),1);
set(gca,'xtick',[]);
hnew = text(xDateEpoch, yposition, labelsVrX);% 5 minutes!! / 4000 dates
set(hnew,'fontsize',14);
set(hnew,'rotation',90,'horizontalalignment','right');
endfunction
To my understanding there should be one empty space between numbers and units.
However, in gnuplot the prefix %c (see help format specifiers) for numbers from 1-999 apparently seems to be ' ' instead of ''.
So, in the example plot below neither the xtic labels nor the ytic labels are all correct.
Either you have some tic labels with zero space or with two spaces, but not all with one. It's a detail and maybe some people won't even notice, but if possible I would prefer to do it the correct way.
Quite some time ago I placed a bug report, but no response so far.
Is there maybe an immediate workaround?
Code:
### wrong prefix for %c 1-999
reset session
set size ratio -1
set logscale xy
set format x "%.0s%cΩ" # no space
set format y "%.0s %cΩ" # one space
set xrange [1e-3:1e12]
set grid x, y
plot x
### end of code
Result:
Addition:
Based on the answer of #maij pointing to the source code, here is a gnuplot attempt to "fix" this, which should be easily transferred to C.
Code:
### gnuplot "fix" for prefix for %c 1-999
prefix(power) = "yzafpnum kMGTPEZY"[(power+24)/3+1 : (power+24)/3 + sgn(abs(power))]
do for [power=-24:24:3] {
print sprintf("% 3g '%s'", power, prefix(power))
}
### end of code
Result:
-24 'y'
-21 'z'
-18 'a'
-15 'f'
-12 'p'
-9 'n'
-6 'u'
-3 'm'
0 '' # zero length
3 'k'
6 'M'
9 'G'
12 'T'
15 'P'
18 'E'
21 'Z'
24 'Y'
It looks like a bug.
This is the workaround I came up it. With this, there is always one space between the number and the units.
You can tell gnuplot where to set each tick mark individually and what each label should be, with set xtics add. The function gprintf can format a number using gnuplot specifiers.
Since we already know what value each tick should have, it's easy to set them with a loop.
# Function to choose the right label format.
f(x) = (x < 1000 && x >= 1) ? gprintf("%.0f Ω", x) : gprintf("%.0s %cΩ", x)
# Loop to set each tick mark and label.
do for [i=-2:12:2] {
set xtics add (f(10**i) 10**i)
set ytics add (f(10**i) 10**i)
}
set size ratio -1
set logscale xy
set xrange [1e-3:1e12]
set grid x, y
plot x
Fix the source code:
Currently I have only source code of the Debian Stretch gnuplot version 5.0.5, linenumbers etc. might be outdated.
The problem is in the function gprintf in util.c around line 868:
power = (power + 24) / 3;
snprintf(dest, remaining_space, temp, "yzafpnum kMGTPEZY"[power]);
Here we see the following:
the letters in yzafpnum kMGTPEZY are the scientific prefixes.
From 1 - 999, power = (0 + 24) / 3 = 8
and yzafpnum kMGTPEZY"[8] is the additional space
I am not a C expert, but as a first attempt, I changed this to
power = (power + 24) / 3;
if (power != 8) {
snprintf(dest, remaining_space, temp, "yzafpnum kMGTPEZY"[power]);
} else {
snprintf(dest, remaining_space, "%s", "");
}
(The gprintf function expects at least a terminating null character to be printed, most probably there are simpler solutions.)
A workaround without recompiling:
What about setting the tic labels manually? But I think you came up with this idea before and don't like it.
This is quite a tough challenge I have with my code. First of all the code I am putting here is not runnable because I am using an Excel sheet (but I am happy to email it if people want to try using my code).
What I have is an Excel sheet with data on cross-sectional fibres in a microscopic image I took. The information is basically: location of the section, area, angle of rotation.
From that I calculate the angle of orientation Phi, and Gamma. After that I use the scatter function to plot a dot of different colors for each Phi angle value. I use a constant color for a range of 10 degrees. Which gives me a picture like this:
Now my aim to is calculate the area of each homogeneous region. So I look for a way to plot let's say all the dots within the -10 +10 region (I'm doing 20 degrees for now, but will do 10 after). I used a look and I get a picture like this:
The white corresponds where the dots are within the range I selected. After that I use the toolbox in MATLAB to convert each dot into a pixel. So I'll get a black background with loads of white pixels, then I use imdilate to make circles, fill holes and isolate each region with a specific color. Finally I use the functions boundary and patch, to create each boundary and fill them with a color. And I get a picture like this:
Which is what I want and I can get the area of each region and the total area (I used a threshold to discard the small areas). Then I run the code several time for each region, and I use imfuse to put them back together and see what it looks like.
THE PROBLEM is, they overlap quite a lot, and that is because there are some errors in my data, and therefore some blue dots will be in the red and so on.
So I want to run the code once, then when I rerun it with another range, it does the same thing but doesn't take into account value when there's already something plotted before.
I tried to do that by, after running once, saving the matrix bw4 and adding a condition when plotting the black and white pic, by saying if Phi is in my range AND there no white here then you can put white, otherwise it's black. But it doesn't seem to work.
I understand this is quite a complicated thing to explain, but I would appreciate any ideas, and open to chat via email or otherwise. I am putting the full code now, and I can send you my Excel sheet if you want to run it on your computer and see for yourself.
clearvars -except data colheaders bw4
close all
clc
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% CHANGE DATA FOR EACH SAMPLE %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
cd 'C:\Users\dkarta\Desktop\Sample 12\12.6'
data=xlsread('Sample12_6res.xlsx');
cd 'C:\Users\dkarta\Documents\MATLAB'
%data=Sample121res; % Data name
imax=length(data); % Numbers of rows in data sheet
y=11900; % Number of pixels in the y on image j
%%
data(:,15)=data(:,9)*pi/180; % Convers Column 9 (angle of rotation) in rads
data(:,16)=y-data(:,6); % Reset the Y coordinate axis to bottom left
delta = 0 : 0.01 : 2*pi; % Angle in paramteric equations
theta=45*pi/180; % Sample cutting angle in rads
%AA=[data(:,5)' data(:,16)' phi']
% Define colors
beta=acos(data(1:imax,8)./data(1:imax,7));%./acos(0);
phi=atan(sin(beta).*cos(data(1:imax,15))./(sin(theta)*sin(beta).*sin(data(1:imax,15))+cos(theta)*cos(beta)))/(pi/2);
phi2=phi/2+1/2; % Scales in plane angle phi between 0 and 1
gamma=atan((cos(theta)*sin(beta).*sin(data(1:imax,15))-sin(theta)*cos(beta))./...
(sin(theta)*sin(beta).*sin(data(1:imax,15))+cos(theta)*cos(beta)))/(pi/2);
gamma2=gamma+1/2; % Scales out of plane angle gamma between 0 and 1
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% MESHGRID AND COLOURMAP %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
x1=data(1:imax,5);
y1=data(1:imax,16);
z1=phi*90;
z2=gamma*90;
n=300;
%Create regular grid across data space
[X,Y] = meshgrid(linspace(min(x1),max(x1),n), linspace(min(y1),max(y1),n));
% Creating a colormap with 10 degree constant colors
map4=[0 0 1;0 1/3 1;0 2/3 1; 0 1 1;0 1 2/3;0 1 1/3;0 1 0;1/3 1 0;2/3 1 0;1 1 0;1 0.75 0;1 0.5 0;1 0.25 0;1 0 0;0.75 0 0.25;0.5 0 0.5;0.25 0 0.75; 0 0 1];
Colormap4=colormap(map4);
h=colorbar;
caxis([-90 90])
set(h, 'YTick', [-90:10:90])
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% PLOT USING SCATTER - ISOLATE SOME REGIONS %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
a=-10; % Lower boundary for angle interval
b=10; % Upper boundary for angle interval
c=z1>a & z1 < b;
c=c.*1;
%j=1;
y1=(y1-min(y1)+1);
y2=max(y1)-y1+1;
[X1,Y1]=meshgrid(1:500,1:500);
griddata(x1,y2,c,X1,Y1);
clear c1
for i=1:imax
if z1(i)< b && z1(i)> a %&& bw4(round(y1(i)),round(x1(i))) == 0
c(i) = 1;
c1(round(y2(i)),round(x1(i)))=1;
else
c(i)= 0;
c1(round(y2(i)),round(x1(i)))=0;
end
end
C=[c c c];
%c(find(c==0)) = NaN;
%contourf(X,Y,griddata(x1,y1,c,X,Y),100,'EdgeColor', 'None')
figure(1), scatter(x1,y1,3,z1,'filled');
axis equal
axis ([0 8000 0 12000])
axis off
figure(2), scatter(x1,y1,3,C,'filled');
axis equal
axis ([0 8000 0 12000])
axis off
se=strel('disk',50,8);
bw2=imdilate(c1,se);
bw4=bwlabel(bw2);
bw3=imfill(bw4,'holes');
max(bw4(:));
figure(3),imshow(c1,'InitialMagnification', 10);
figure(4), imshow(bw2,'InitialMagnification', 10);
figure(5), imshow(bw3,'InitialMagnification', 10);
figure(6),imshow(label2rgb(bw4),'InitialMagnification', 10);
k=ones(max(bw4(:)),1);
clear bw5
for i=1:length(x1)
if bw3(round(y2(i)),round(x1(i))) ~= 0
m=bw3(round(y2(i)),round(x1(i)));
bw5{m}(k(m),1)=x1(i); bw5{m}(k(m),2)=y2(i);
k(m)=k(m)+1;
end
end
figure(7), imshow(~c1,'InitialMagnification', 10);
hold on
for i=1:max(bw4(:))
%scatter(bw5{i}(:,1),bw5{i}(:,2))
j = boundary(bw5{i}(:,1),bw5{i}(:,2),0.5);
%poly=convhull(bw5{i}(:,1),bw5{i}(:,2));
%plot(bw5{i}(poly,1),bw5{i}(poly,2)), title('convhull')
if polyarea(bw5{i}(j,1),bw5{i}(j,2))> 10^5;
patch(bw5{i}(j,1),bw5{i}(j,2),'r'), title('boundary')
indexminy(i)=find(min(bw5{i}(:,2)) == bw5{i}(:,2));
indexminx(i)=find(min(bw5{i}(:,1)) == bw5{i}(:,1));
indexmaxy(i)=find(max(bw5{i}(:,2)) == bw5{i}(:,2));
indexmaxx(i)=find(max(bw5{i}(:,1)) == bw5{i}(:,1));
%xmin = bw5{i}(indexminx); xmax = bw5{i}(indexmaxx);
%ymin = bw5{i}(indexminy); ymax = bw5{i}(indexmaxy);
str=[(indexminx(i)+indexmaxx(i))/2,(indexminy(i)+indexmaxy(i))/2,'Region no.',num2str(i)];
text((min(x1(i))+max(x1(i)))/2,(min(y1(i))+max(y1(i)))/2,str)
polya(i)=polyarea(bw5{i}(j,1),bw5{i}(j,2));
end
end
spolya=sum(polya(:))
print -dpng -r500 B
Just to show you more pictures of when I fuse several of them:
And when I fuse:
As you can see they overlap, which I don't want, so I want each image that I create to 'know' what I'm doing on the previous runs so that it doesn't overlap. I want to get the percentage area of each region and if they overlap I cannot use the actual total area of my sample and the results are wrong.
I dont have my matlab working but here is what you need to do.
For the first run make an array of zeros equal to your image size
already_taken = zeros(size(bw3));
Then on each run, you can fill up the regions taken by this iteration. So at the end of your code, where you save the output to a png, read it back into something like
this_png = rgb2gray(imread(current_png_path))>threshold;
Convert this into a logical array by doing some thresholding and add these values into already taken. So at the end of the code, do a
already_taken = already_taken | this_png; % You might need to check if you need a single | or a double ||
So now you have an image of already taken pixels, ill bake sure I don't allow bw2 to take these values at first place
bw2(already_taken) = 0;
And at the end of the code when I want to write my png, my smart boundary creation might again have entered into already_taken area so there again I'll have to put some check. As far as I understand, this boundary is being created based upon your bw5. So where ever you fill this matrix, try putting a similar check as I did above for bw2.
I hope this helps.
I am trying to get a y-tick at "zero" for a multi-series d3 plot. My x-axis is a time scale and y-axis is some random data-scale. Here is my plunkr
http://plnkr.co/edit/emOKcxrHP7gU1U1l0Mff?p=preview
If I just add zero to the y-tick values, it does not work (i.e. in the following function if I say var yTickValues=[0] ) and it messes up my plot (draws another x-axis below the existing one)
function getYTickValues(){
var deltaY = Math.round((maxY - minY)/(yTickCount-1));
var yTickValues = [];
for(var i=0;i<yTickCount;i++){
yTickValues.push(((minY + i * deltaY) * 100) / 100);
};
return yTickValues;
}
I am unable to figure out how to fix this so I can always get a y-tick at zero. I would like to not touch my minX, maxX, minY and maxY because the domain range scale will change for the sake of accommodating the zero y-tick.
Any help is appreciated.
Change the y domain to start at 0:
y.domain([0, maxY]);
and then also including 0 in the yTickValues array as you suggest above:
var yTickValues = [0];
The data values still remain between minY and maxY, but the y-axis runs to 0. I think that's what the question was getting at?
I also made a couple of changes to the getYTickValues() function to evenly space the rest of the y tick values. See http://plnkr.co/edit/q6XnujIyB8JdzN8AA88j?p=preview
I know how to create a histogram (just use "with boxes") in gnuplot if my .dat file already has properly binned data. Is there a way to take a list of numbers and have gnuplot provide a histogram based on ranges and bin sizes the user provides?
yes, and its quick and simple though very hidden:
binwidth=5
bin(x,width)=width*floor(x/width)
plot 'datafile' using (bin($1,binwidth)):(1.0) smooth freq with boxes
check out help smooth freq to see why the above makes a histogram
to deal with ranges just set the xrange variable.
I have a couple corrections/additions to Born2Smile's very useful answer:
Empty bins caused the box for the adjacent bin to incorrectly extend into its space; avoid this using set boxwidth binwidth
In Born2Smile's version, bins are rendered as centered on their lower bound. Strictly they ought to extend from the lower bound to the upper bound. This can be corrected by modifying the bin function: bin(x,width)=width*floor(x/width) + width/2.0
Be very careful: all of the answers on this page are implicitly taking the decision of where the binning starts - the left-hand edge of the left-most bin, if you like - out of the user's hands. If the user is combining any of these functions for binning data with his/her own decision about where binning starts (as is done on the blog which is linked to above) the functions above are all incorrect. With an arbitrary starting point for binning 'Min', the correct function is:
bin(x) = width*(floor((x-Min)/width)+0.5) + Min
You can see why this is correct sequentially (it helps to draw a few bins and a point somewhere in one of them). Subtract Min from your data point to see how far into the binning range it is. Then divide by binwidth so that you're effectively working in units of 'bins'. Then 'floor' the result to go to the left-hand edge of that bin, add 0.5 to go to the middle of the bin, multiply by the width so that you're no longer working in units of bins but in an absolute scale again, then finally add back on the Min offset you subtracted at the start.
Consider this function in action:
Min = 0.25 # where binning starts
Max = 2.25 # where binning ends
n = 2 # the number of bins
width = (Max-Min)/n # binwidth; evaluates to 1.0
bin(x) = width*(floor((x-Min)/width)+0.5) + Min
e.g. the value 1.1 truly falls in the left bin:
this function correctly maps it to the centre of the left bin (0.75);
Born2Smile's answer, bin(x)=width*floor(x/width), incorrectly maps it to 1;
mas90's answer, bin(x)=width*floor(x/width) + binwidth/2.0, incorrectly maps it to 1.5.
Born2Smile's answer is only correct if the bin boundaries occur at (n+0.5)*binwidth (where n runs over integers). mas90's answer is only correct if the bin boundaries occur at n*binwidth.
Do you want to plot a graph like this one?
yes? Then you can have a look at my blog article: http://gnuplot-surprising.blogspot.com/2011/09/statistic-analysis-and-histogram.html
Key lines from the code:
n=100 #number of intervals
max=3. #max value
min=-3. #min value
width=(max-min)/n #interval width
#function used to map a value to the intervals
hist(x,width)=width*floor(x/width)+width/2.0
set boxwidth width*0.9
set style fill solid 0.5 # fill style
#count and plot
plot "data.dat" u (hist($1,width)):(1.0) smooth freq w boxes lc rgb"green" notitle
As usual, Gnuplot is a fantastic tool for plotting sweet looking graphs and it can be made to perform all sorts of calculations. However, it is intended to plot data rather than to serve as a calculator and it is often easier to use an external programme (e.g. Octave) to do the more "complicated" calculations, save this data in a file, then use Gnuplot to produce the graph. For the above problem, check out the "hist" function is Octave using [freq,bins]=hist(data), then plot this in Gnuplot using
set style histogram rowstacked gap 0
set style fill solid 0.5 border lt -1
plot "./data.dat" smooth freq with boxes
I have found this discussion extremely useful, but I have experienced some "rounding off" problems.
More precisely, using a binwidth of 0.05, I have noticed that, with the techniques presented here above, data points which read 0.1 and 0.15 fall in the same bin. This (obviously unwanted behaviour) is most likely due to the "floor" function.
Hereafter is my small contribution to try to circumvent this.
bin(x,width,n)=x<=n*width? width*(n-1) + 0.5*binwidth:bin(x,width,n+1)
binwidth = 0.05
set boxwidth binwidth
plot "data.dat" u (bin($1,binwidth,1)):(1.0) smooth freq with boxes
This recursive method is for x >=0; one could generalise this with more conditional statements to obtain something even more general.
We do not need to use recursive method, it may be slow. My solution is using a user-defined function rint instesd of instrinsic function int or floor.
rint(x)=(x-int(x)>0.9999)?int(x)+1:int(x)
This function will give rint(0.0003/0.0001)=3, while int(0.0003/0.0001)=floor(0.0003/0.0001)=2.
Why? Please look at Perl int function and padding zeros
I have a little modification to Born2Smile's solution.
I know that doesn't make much sense, but you may want it just in case. If your data is integer and you need a float bin size (maybe for comparison with another set of data, or plot density in finer grid), you will need to add a random number between 0 and 1 inside floor. Otherwise, there will be spikes due to round up error. floor(x/width+0.5) will not do because it will create pattern that's not true to original data.
binwidth=0.3
bin(x,width)=width*floor(x/width+rand(0))
With respect to binning functions, I didn't expect the result of the functions offered so far. Namely, if my binwidth is 0.001, these functions were centering the bins on 0.0005 points, whereas I feel it's more intuitive to have the bins centered on 0.001 boundaries.
In other words, I'd like to have
Bin 0.001 contain data from 0.0005 to 0.0014
Bin 0.002 contain data from 0.0015 to 0.0024
...
The binning function I came up with is
my_bin(x,width) = width*(floor(x/width+0.5))
Here's a script to compare some of the offered bin functions to this one:
rint(x) = (x-int(x)>0.9999)?int(x)+1:int(x)
bin(x,width) = width*rint(x/width) + width/2.0
binc(x,width) = width*(int(x/width)+0.5)
mitar_bin(x,width) = width*floor(x/width) + width/2.0
my_bin(x,width) = width*(floor(x/width+0.5))
binwidth = 0.001
data_list = "-0.1386 -0.1383 -0.1375 -0.0015 -0.0005 0.0005 0.0015 0.1375 0.1383 0.1386"
my_line = sprintf("%7s %7s %7s %7s %7s","data","bin()","binc()","mitar()","my_bin()")
print my_line
do for [i in data_list] {
iN = i + 0
my_line = sprintf("%+.4f %+.4f %+.4f %+.4f %+.4f",iN,bin(iN,binwidth),binc(iN,binwidth),mitar_bin(iN,binwidth),my_bin(iN,binwidth))
print my_line
}
and here's the output
data bin() binc() mitar() my_bin()
-0.1386 -0.1375 -0.1375 -0.1385 -0.1390
-0.1383 -0.1375 -0.1375 -0.1385 -0.1380
-0.1375 -0.1365 -0.1365 -0.1375 -0.1380
-0.0015 -0.0005 -0.0005 -0.0015 -0.0010
-0.0005 +0.0005 +0.0005 -0.0005 +0.0000
+0.0005 +0.0005 +0.0005 +0.0005 +0.0010
+0.0015 +0.0015 +0.0015 +0.0015 +0.0020
+0.1375 +0.1375 +0.1375 +0.1375 +0.1380
+0.1383 +0.1385 +0.1385 +0.1385 +0.1380
+0.1386 +0.1385 +0.1385 +0.1385 +0.1390
Different number of bins on the same dataset can reveal different features of the data.
Unfortunately, there is no universal best method that can determine the number of bins.
One of the powerful methods is the Freedman–Diaconis rule, which automatically determines the number of bins based on statistics of a given dataset, among many other alternatives.
Accordingly, the following can be used to utilise the Freedman–Diaconis rule in a gnuplot script:
Say you have a file containing a single column of samples, samplesFile:
# samples
0.12345
1.23232
...
The following (which is based on ChrisW's answer) may be embed into an existing gnuplot script:
...
## preceeding gnuplot commands
...
#
samples="$samplesFile"
stats samples nooutput
N = floor(STATS_records)
samplesMin = STATS_min
samplesMax = STATS_max
# Freedman–Diaconis formula for bin-width size estimation
lowQuartile = STATS_lo_quartile
upQuartile = STATS_up_quartile
IQR = upQuartile - lowQuartile
width = 2*IQR/(N**(1.0/3.0))
bin(x) = width*(floor((x-samplesMin)/width)+0.5) + samplesMin
plot \
samples u (bin(\$1)):(1.0/(N*width)) t "Output" w l lw 1 smooth freq