Edit: while the solution suggested works this format is problematic when I have many long strings, any suggestions?
Lets say I have categorized a vector of M stings into N groups. Meaning each of the M strings is assigned a number between 1 to N indicating the category the string belongs into. For example if M=6 and N=3 I might have:
v = [ 'a' ; 'b' ; 'c' ; 'd' ; 'e' ; 'f' ]
c = [ 1 ; 2 ; 1 ; 1 ' 3 ; 2 ]
which indicates that a, c and d were all categorized to group "1". "e" was categorized to group 3.
I want to somehow plot - using Matlab - this categorization.
I am trying something along the lines of:
plot(v,'b--o')
set(gca,'xticklabel',c.')
but I need the plot to look more like a scatter, sadly it seems scatter does not work with strings. Any suggestions?
Plus, the vector of strings might get very long, anyone knows how to make the plot scrollable?
The following gets you a barchart with your names as x-axis labels. Uncomment the other line for a scatterplot. In general, such a visualisation is probably not the right format for extremely many words (very high M).
v = [ 'a' ; 'b' ; 'c' ; 'd' ; 'e' ; 'f' ];
c = [ 1 ; 2 ; 1 ; 1 ; 3 ; 2 ];
bar(c)
% scatter(1:length(c), c) % use this for a scatter plot
set(gca, 'xticklabel', v)
bar is usually slow. You can get a similar result more quickly and without Matlab binning things funnily by using plot.
Edit: I think you wanted the strings on the y axis.
plot(c,'bo')
ax = gca;
ax.XTick = 1:length(c);
ax.YTick = 0:max(c);
set(ax,'xticklabel',v)
view(-90,90)
Related
I have some data I want to draw in bars.
Let says I have a group of 3 values for two conditions and a group of 2 values for the same conditions
y3 = [ 1, 1, 1; 0, 1, 2];
y2 = [1 , 1 ; 0, 2];
I know how to use plotBarStackGroups (https://fr.mathworks.com/matlabcentral/fileexchange/32884-plot-groups-of-stacked-bars) to build the bars.
The code to build the bars (maybe it's not the best way)
B = floor(rand(2,2,3));
B(1,:,:) = [[y2(1,:),0] ; y3(1,:)];
B(2,:,:) = [[y2(2,:),0] ; y3(2,:)];
label = {'condition 1', 'condition 2'};
plotBarStackGroups(B,label);
But I want to obtain something like this
and I see two problems : legend by groups (or not prevent the colors to be shared) and the fact that the groups don't have the same length.
I could do two different figures but if I can regroup them, I think it's better
If I correctly got your question
vals = {rand(3,3) rand(4,3)}; %sample data
max_len = max(cellfun(#(x) size(x,1), vals));
pad_vals = cellfun(#(x) cat(1,x,nan(max_len-size(x,1),size(x,2))), vals, 'un',0);
pad_vals = cat(3,pad_vals{:});
figure,
subplot(1,2,1), bar(vals{1},'stacked'), set(gca,'Ylim',[0 3]),
subplot(1,2,2),bar(vals{2},'stacked'), set(gca,'Ylim',[0 3])
%I slightly modified plotBarStackGroups to output bar handles
h=plotBarStackGroups(permute(pad_vals,[1 3 2]),{'1','2','3','4'}) %reorder the matrix according to the function needs
cgroup={'r','m','y';'k','b','c'};
cgroup=cgroup'; h=h'; % For comfortable iteration
for k=1:numel(h),
set(h(k),'Facecolor',cgroup{k}),
end
legend({'1','2','3','4','5','6'})
This question already has answers here:
Generate a matrix containing all combinations of elements taken from n vectors
(4 answers)
Closed 5 years ago.
I want to create all pattern combination which possible occur
For example, I have three ball and I want to pick 3 times. All possible is 27 events but I want to create all possible event in array like this
[1 1 1; 1 1 2; 1 1 3; 1 2 1 ;....]
Dose anyone can help me to write m-file in matlab program, please?
This can be done very easily with base conversion.
If the number of balls does not exceed 10
M = 3; % Number of balls. Must not exceed 10
N = 3; % Number of draws
result = dec2base(0:M^N-1, M)-'0'+1;
Note that dec2base ouputs chars, not numbers, and hence the -'0' part. Characters in arithmetic operations behave like their corresponding ASCII codes. So subtracting '0' transforms characters '0', '1', ..., '9' into the corresponding numbers.
With this approach M cannot exceed 10 because then dec2bin would output '0', '1', ..., '9', 'A', 'B' ... and the character arithmetic would not give the correct result for 'A', 'B', ... But this can be easily solved as follows.
For number of balls up to 36
M = 12; % Number of balls. Must not exceed 36
N = 2; % Number of draws
result = dec2base(0:M^N-1, M)-'0'+1; % same as before
result = result - ('A'-'9'-1)*(result>10); % correction
The new line simply corrects the results for 'A', 'B', ... by subtracting 'A'-'9'-1, to compensate the fact that '9' and 'A' do not have consecutive ASCII codes.
With this approach M cannot exceed 36 because of dec2base restrictions.
You can create the result like this for the specific case:
firstCol = [ones(9,1);2*ones(9,1);3*ones(9,1)];
secondCol = repeat([ones(3,1);2*ones(3,1);3*ones(3,1)],1,3);
thirdCol = repeat([1;2;3],1,9);
result = [firstCol secondCol thirdCol];
First, repeat 9 times 1,2, and 3 for first column. then repeat each of them 3 times for the second column and choose the third column once for each item. Indeed, this generate the all possible choices for each location.
How? If you suppose the first element is 1, you have 3 choice for the second place, and 3 choice for the third place. Hence, you have 9 possible option when the first place is 1. Also, fix the second place, and analyze this. You can generalize this for 2 and 3. The above code, try to generate possibilities based on this explanation.
In the above, ones generate a matrix which all elements are 1 with the specified size and repeat function repeats the specified matrix in the specified size and dimension. You can check the documentation to know more about them.
Hence, You can generalize it for n like the following:
n = 10;
result = zeros(3^n,3);
for idx = 1:n
result(:,idx) = repeat([ones(3^(n-idx),1);2*ones(3^(n-idx),1);3*ones(3^(n-idx),1)],1,3^(idx-1));
end
I am trying to give legend to the whole plot. I know it sounds easy for many cases. But it makes me puzzled in this particular case.
figure;
p1 = plot(1:3,[y1,y2,y3],1:2,y4,1:3,[y5,y6,y7,y8,y9])
% Add lines
hold on
h1 = line([1 2 3],[10 10 10]);
h2 = line([1 2 3],[100 100 100]);
% Set properties of lines
set([h1 h2],'LineStyle','none')
% Add a patch
p2 = patch([1 3 3 1],[10 10 100 100],[.85 .85 .85],'LineStyle','none','FaceAlpha',0.5,'DisplayName','Lab Measurement');
hold off
set(gca, 'children',flipud(get(gca,'children')),'XTickLabel', {'L1' ' ' ' ' ' ' ' ' 'L2' ' ' ' ' ' ' ' ' 'L3'},'YScale', 'log')
NameArray = {'Marker','Color','LineStyle','DisplayName'};
ValueArray = {'o','[0.2 0.2 0.2]','-','Var1';...
'+','[0.2 0.2 0.2]','-','Var2';...
'*','[0.2 0.2 0.2]','-','Var3';...
'.','[0.2 0.2 0.2]','-','Var4';...
'x','[0.2 0.2 0.2]','-','Var5';...
's','[0.2 0.2 0.2]','-','Var6';...
'd','[0.2 0.2 0.2]','-','Var7';...
'^','[0.2 0.2 0.2]',':','Var8';...
'h','[0.2 0.2 0.2]','-.','Var9'};
set(p1,NameArray,ValueArray)
When I tried to reveal the legend by giving
legend(p1)
or
legend(p2)
This is what looks like when I try legend(p2)
It just did fine for each part, but not together.
I also tried by giving the legend in command
legend([p2 p1],{'Lab Measurement','Var1','Var2','Var3','Var4','Var5','Var6','Var7','Var8','Var9'})
or
legend([p2 p1],{'Lab Measurement',{'Var1','Var2','Var3','Var4','Var5','Var6','Var7','Var8','Var9'}})
It did not work. Any help would be greatly appreciated!
Per the documentation for plot:
h = plot(___) returns a column vector of chart line objects.
When multiple plot pairs are provided, as in your case, the return from plot is said array of objects:
>> a = plot(1, 2, 1, 2)
a =
2×1 Line array:
Line
Line
The bracket notation for concatenation [] generally implies the user wants to create a row vector, MATLAB does not make assumptions in the case of a vector and a scalar. This means it attempts to use horzcat to concatenate the arrays, which, logically, throws an error.
>> b = plot(1, 2); c = [a b];
Error using horzcat
Dimensions of matrices being concatenated are not consistent.
You'll need to explicitly tell MATLAB that you want to vertically concatenate these, or transpose the column vector into a row vector.
>> c = vertcat(a, b)
c =
3×1 Line array:
Line
Line
Line
or:
>> c = [a.' b]
c =
1×3 Line array:
Line Line Line
Both of which are compatible with legend.
Yay.
Just use
p = vertcat(p2,p1);
legend(p)
Problem solved. Thanks!
I am writing a mesh smoother in Matlab for a particular CFD code. The requirements for the mesh data file are very strict, and so the number format must be very specific. The definition for one quadrilateral element is as follows (Where the 2nd row is the x coordinates and 3rd row is y coordinates).
ELEMENT 14 [ 1Q] GROUP 0
5.000000 10.00000 10.00000 5.000000
2.000000 2.000000 4.500000 4.500000
As you can see, each number takes up exactly 8 characters. Once the whole mesh has been passed through my smoother, I need to write the numbers back to a file. the closest I've gotten to this number format is with the following operator:
%#7.7g
I don't think I can use %f, as this specifies the number of digits after the decimal, which in my case varies (I have coordinates that go from less than one to over 100). the only issue that this operator is giving me is when I have numbers less than one, it only retains 7 sig figs, and the number ends up being 9 characters long; for example:
0.9313373
Does anyone know the correct operator, or have a workaround? Much appreciated.
Single format spec:
If you can live with only 4 digit precision (decimal part after the .) out of your 8 characters, and if the mesh reading program can handle padding 0 (i.e. if it can read values like 007.2365 properly), then the simplest and quickest option is to use only the format specifier.
For example, let's define an input a with different order of magnitude:
a=[ 1 234 7 ;
12 2 0.123456789 ;...
5 2.36 0.0024 ] ;
Your highest number is 234, so that leaves 4 digits for the fractional part. If you accept that precision for all the numbers in your Matrix, then you're all set with a simple instruction:
fmt='%08.4f %08.4f %08.4f\n'; %// hard coded format specifier
sprintf(fmt,a.') %'// we transpose a to keep the same shape in the output since sprintf is column major.
ans =
001.0000 234.0000 007.0000
012.0000 002.0000 000.1235
005.0000 002.3600 000.0024
If you don't know in advance what will be the maximum order of magnitude of your data you can set the format specifier programmatically:
nDigitTotal = 8 ;
nmax = ceil( log10(max(a(:))) ) ; %// max order of magnitude of numbers in "a"
f = sprintf('%%0%d.%df',nDigitTotal,nDigitTotal-nmax-1) ; %// format spec for 1 number
fmt = [f '\t' f '\t' f '\n'] ; %// format spec for a line of 3 numbers
s = sprintf(fmt,a.')
Will give the same result as above. Add a check to make sure there are no extreme values in your matrix which will eat your precision.
Individual format spec:
Lastly, if that precision and/or the leading zero do not work for you, you can resort to a more elaborate solution. I quite like the idea from excaza of setting a mask to specify the precision for each number. I'll produce my own version, very slightly different, which account for numbers at any precision and allow array output. However, if you end up using this solution give credit to excaza since he was the inspiration for this evolution:
a = a.' ; %'// transpose from beginning/ thats done
nDigitTotal = 8; %// Total number of characters
mask = nDigitTotal(ones(size(a))) ; %// Create mask
nOrder = floor( log10(a) ) ; %// find order of magnitude of each element in the matrix
mask = mask - nOrder.*(nOrder>0) -1 ; %// adjust mask according to "nOrder" (only for nOrder>0)
temp = [mask(:)';a(:)']; %// Stack the vectors and weave them
f = '%7.*f' ; %// basic format spec
fmt = [f '\t' f '\t' f '\n'] ; %// build your line
sprintf(fmt,temp) %// go for it
will give you:
ans =
1.0000000 234.00000 7.0000000
12.000000 2.0000000 0.1234568
5.0000000 2.3600000 0.0024000
note: replace the tabulation ('\t') with normal whitespace (' ') in the format specifier separator depending on what your meshing software is expecting.
This is the only workaround I could think of:
A = [1.12341234 .12341234 20.12341234 5 10];
% Create a precision mask
maxwidth = 8; % Max width, characters
mask = maxwidth(ones(size(A))) - 1; % Initialize mask, account for decimal
mask(A < 1) = maxwidth - 2;
% Stack the vectors and weave them
temp = [mask(:)';A(:)'];
temp = temp(:);
test = sprintf('%#7.*g ', temp);
Which returns:
test =
1.123412 0.123412 20.12341 5.000000 10.00000
It's an annoying extra step but we can utilize sprintf's ability to take an asterisk in order to refer to an argument in the input list. Due to how my sprintf call and test case are set up, I wove the mask and data together so sprintf sees the precision specifier and data alternating. The temp(:) call isn't necessary, if you pass the original temp matrix to sprintf it will do the same thing since it reads the data column-major. I added it in so the behavior is more explicit.
How to formulate the sprintf call for your actual printing routine will depend on what you're doing, but this should at least help you on your way.
Edit1: To expand, what the above is doing is equivalent to:
a = sprintf('%#7.*g ', temp(1), temp(2));
b = sprintf('%#7.*g ', temp(3), temp(4));
c = sprintf('%#7.*g ', temp(5), temp(6));
d = sprintf('%#7.*g ', temp(7), temp(8));
e = sprintf('%#7.*g ', temp(9), temp(10));
test = [a b c d e];
Edit2: Updated to account for integer values
Edit3: Note that this currently will only work for positive numbers
What I want to do is to sort these coordinates points:
Measured coordinates (x,y)= (2,2),(2,3),(1,2),(1,3),(2,1),(1,1),(3,2),(3,3),(3 ,1)
I need to get sequences or trajectories of this points to follow them by iteration.
data = [2,2 ; 2,3 ; 1,2 ; 1,3 ; 2,1 ; 1,1 ; 3,2 ; 3,3 ; 3 ,1]
% corresponding sort-value, pick one out or make one up yourself:
sortval = data(:,1); % the x-value
sortval = data(:,2); % y-value
sortval = (data(:,1)-x0).^2 + (data(:,2)-y0).^2; % distance form point (xo,y0)
sortval = ...
[~,sortorder] = sort(sortval);
sorted_data = data(sortorder,:);
But from you comment, I understand you actually need something to reconstruct a path and iteratively find the closest neighbour of the last found point (of the reconstructed path so far).
The following is how I would solve this problem (using pdist2 for calculating the distances between all the points for easiness):
data = [2,2 ; 2,3 ; 1,2 ; 1,3 ; 2,1 ; 1,1 ; 3,2 ; 3,3 ; 3 ,1];
dist = pdist2(data,data);
N = size(data,1);
result = NaN(1,N);
result(1) = 1; % first point is first row in data matrix
for ii=2:N
dist(:,result(ii-1)) = Inf;
[~, closest_idx] = min(dist(result(ii-1),:));
result(ii) = closest_idx;
end
which results in:
result =
1 2 4 3 6 5 9 7 8
being the indices to consecutive points on the curve. Here's a plot of this result:
As #mathematician1975 already mentioned, there can be equal distances to a point. This is solved here by using min which just finds the first occurrence of the minimum in an array. This means that if you order your input data differently, you can get different results of course, this is inherent to the equal-distance issue.
2nd remark: I don't know how this will behave when using large input data matrices, probably a bit slow because of the loop, which you can't avoid. I still see room for improvement, but that's up to you ;)
Create a matrix from your points so that you have something like
A = [2 2 1 1 2 1 3 3 3;
2 3 2 3 1 1 2 3 1]';
then try
B = sortrows(A,1);
to get a matrix with rows that are your points ordered by xvalue or
B = sortrows(A,2)
to get a matrix with rows that are your points ordered by their 'y' value. If your points are ordered with respect to some other ordering parameter (such as time) then sorting will not work unless you remember the order that they were created in.