I'm using MATLAB and trying to run a randomized block of different conditions for a psych experiment. I have 'levels' I want to try out, 3 times each. So I would like to basically permute three copies of the vector, all together. So far I have:
levels = [0 0.25 0.5 0.75 1]
permutationIndices = randperm(length(levels)*3)
... and then here the natural solution to me would be a mapping function that maps whatever is in levels to the corresponding location using mod 5, so for instance in permutationIndices, wherever there is a 1, 6, or 11, the number 0 would be slotted in. How can I do this (or, is there a neater way?) Thanks.
mod(randperm(15)-1,5)+1
This outputs
5 4 5 1 3 2 1 1 4 3 3 2 4 5 2
or another run:
3 4 4 2 2 5 3 2 4 1 3 1 5 5 1
which you can use to get the corresponding elements from the levels vector:
output = levels(mod(randperm(15)-1,5)+1)
Try:
%# three copies of levels
x = repmat(1:numel(levels),1,3)
%# random permuation
[~,ord] = sort(rand(size(x)));
output = x(ord)
For example:
output =
3 2 4 2 5 4 5 2 3 1 5 3 1 1 4
The mod approach is probably the simplest. I would generate permuted indices for an array of length nlevels * ntimes, and then mod those indices to index into an array of your actual level values.
level_values = [0 0.25 0.5 0.75 1];
nlevels = numel(level_values);
ntimes = 3;
lv_inds = 1 + mod( randperm(ntimes*nlevels - 1), nlevels);
levels = level_values( lv_inds );
The odd 1 + and - 1 offsets are necessary to make the mod call work with Matlab's array indexing (which starts at 1).
Related
I am new to Matlab and I have a basic question.
I have this data set:
1 2 3
4 5 7
5 2 7
1 2 3
6 5 3
I am trying to calculate the relative frequencies from the dataset above
specifically calculating the relative frequency of x=1, y=2 and z=3
my code is:
data = load('datasetReduced.txt')
X = data(:, 1)
Y = data(:, 2)
Z = data(:, 3)
f = 0;
for i=1:5
if X == 1 & Y == 2 & Z == 3
s = 1;
else
s = 0;
end
f = f + s;
end
f
r = f/5
it is giving me a 0 result.
How can the code be corrected??
thanks,
Shosho
Your issue is likely that you are comparing floating point numbers using the == operator which is likely to fail due to floating point errors.
A faster way to do this would be to use ismember with the 'rows' option which will result in a logical array that you can then sum to get the total number of rows that matched and divide by the total number of rows.
tf = ismember(data, [1 2 3], 'rows');
relFreq = sum(tf) / numel(tf);
I think you want to count frequency of each instance, So try this
data = [1 2 3
4 5 7
5 2 7
1 2 3
6 5 3];
[counts,centers] = hist(data , unique(data))
Where centers is your unique instances and counts is count of each of them. The result should be as follow:
counts =
2 0 0
0 3 0
0 0 3
1 0 0
1 2 0
1 0 0
0 0 2
centers =
1 2 3 4 5 6 7
That it means you have 7 unique instances, from 1 to 7 and there is two 1s in first column and there is not any 1s in second and third and etc.
I am making a 3 dimensional array, using matlab, that progresses according to a pattern. Athough I could write out the Array manually I am sure there is a quicker way to do it.
multiArray = cat(3,...
[1+randn(4,3); 1*randn(4,3)],...
[2+randn(4,3); 2*randn(4,3)],...
[3+randn(4,3); 3*randn(4,3)]);
If I want to make the above array to be 8x3x25 then the last line would be
[25+randn(4,3); 25*randn(4,3)]
But how can I make such an array without going through all the tedious intervening steps?
While mikkola basically got the solution, there is no need to shift dimensions at the end.
s=[4,3,25];
it=reshape(1:s(3),1,1,[]);
out = [bsxfun(#plus , it, randn(s));...
bsxfun(#times, it, randn(s))];
Here's a possible way using bsxfun.
%// 25 x 4 x 3 with elements for i + randn(4,3)
P = bsxfun(#plus, (1:25)', randn(25,4,3));
%// 25 x 4 x 3 with elements for i * randn(4,3)
T = bsxfun(#times, (1:25)', randn(25,4,3));
%// Concatenate and shift dimensions to get desired size output
multiArray = shiftdim([P T], 1);
If you don't mind taking things to 4D for efficiency purposes -
N = 25; %// Number of 3D slices
out = randn(4,2,3,N);
out(:,1,:,:) = bsxfun(#plus,permute(1:N,[1 4 3 2]),out(:,1,:,:));
out(:,2,:,:) = bsxfun(#times,permute(1:N,[1 4 3 2]),out(:,2,:,:));
out = reshape(out,8,3,N);
To legitimize the solution, let's start off with an input of A = randn(8,3,N) and initialize the output out with it. Also, let's take number of 3D slices as a small number, so say N = 3.
Thus,
>> N = 3;
A = randn(8,3,N);
out = reshape(A,[4 2 3 N]); %// This replaces "out = randn(4,2,3,N)"
Next up, we run the code that will change out -
>> out(:,1,:,:) = bsxfun(#plus,permute(1:N,[1 4 3 2]),out(:,1,:,:));
out(:,2,:,:) = bsxfun(#times,permute(1:N,[1 4 3 2]),out(:,2,:,:));
out = reshape(out,8,3,N);
Now, start validating per 3D slice -
>> out(1:4,:,1) - A(1:4,:,1)
ans =
1 1 1
1 1 1
1 1 1
1 1 1
>> out(1:4,:,2) - A(1:4,:,2)
ans =
2 2 2
2 2 2
2 2 2
2 2 2
>> out(1:4,:,3) - A(1:4,:,3)
ans =
3 3 3
3 3 3
3 3 3
3 3 3
>> out(5:end,:,1)./A(5:end,:,1)
ans =
1 1 1
1 1 1
1 1 1
1 1 1
>> out(5:end,:,2)./A(5:end,:,2)
ans =
2 2 2
2 2 2
2 2 2
2 2 2
>> out(5:end,:,3)./A(5:end,:,3)
ans =
3 3 3
3 3 3
3 3 3
3 3 3
In Matlab I have a big matrix containing the coordinates (x,y,z) of many points (over 200000). There is an extra column used as identification. I have written this code in order to sort all coordinate points. My final goal is to find duplicated points (rows with same x,y,z). After sorting the coordinate points I use the diff function, two consecutive rows of the matrix with the same coordinates will take value [0 0 0], and then with ismember I can find which rows of that matrix resulting from applying "diff" have the [0 0 0] row. With the indices returned from ismember I can find which points are repeated.
Back to my question...This is the code I wrote to sort properly my coordintes+id matrix. I guess It could be done better. Any suggestion?
%coordinates are always positive
a=[ 1 2 8 4; %sample matrix
1 0 5 6;
2 4 7 1;
3 2 1 0;
2 3 5 0;
3 1 2 8;
1 2 4 8];
b=a; %for checking purposes
%sorting first column
a=sortrows(a,1);
%sorting second column
for i=0:max(a(:,1));
k=find(a(:,1)==i);
if not(isempty(k))
a(k,:)=sortrows(a(k,:),2);
end
end
%Sorting third column
for i=0:max(a(:,2));
k=find(a(:,2)==i);
if not(isempty(k))
%identifying rows with same value on first column
for j=1:length(k)
[rows,~] = ismember(a(:,1:2), [ a(k(j),1),i],'rows');
a(rows,3:end)=sortrows(a(rows,3:end),1);
end
end
end
%Checking that rows remain the same
m=ismember(b,a,'rows');
if length(m)~=sum(m)
disp('Error while sorting!');
end
Why don't you just use unique?
[uniqueRows, ii, jj] = unique(a(:,1:3),'rows');
Example
a = [1 2 3 5
3 2 3 6
1 2 3 9
2 2 2 8];
gives
uniqueRows =
1 2 3
2 2 2
3 2 3
and
jj =
1
3
1
2
meaning third row equals first row.
If you need the full unique rows, including the fourth column: use ii to index a:
fullUniqueRows = a(ii,:);
which gives
fullUniqueRows =
1 2 3 9
2 2 2 8
3 2 3 6
Trying to sort a based on the fourth column? Do this -
a=[ 1 2 8 4; %sample matrix
1 0 5 6;
2 4 7 1;
3 2 1 0;
2 3 5 0;
3 2 1 8;
1 2 4 8];
[x,y] = sort(a(:,4))
sorted_a=a(y,:)
Trying to get the row indices having repeated x-y-z coordinates being represented by the first three columns? Do this -
out = sum(squeeze(all(bsxfun(#eq,a(:,1:3),permute(a(:,1:3),[3 2 1])),2)),2)>1
and use it similarly for sorted_a.
Hi I need to sort a vector and assign a ranking for the corresponding sorting order. I'm using sort function [sortedValue_X , X_Ranked] = sort(X,'descend');
but the problem is it assigns different ranks for the same values (zeros).
i.e. x = [ 13 15 5 5 0 0 0 1 0 3] and I want zeros to take the same last rank which is 6 and fives needs to share the 3rd rank etc..
any suggestions?
The syntax [sortedValues, sortedIndexes] = sort(x, 'descend') does not return rank as you describe it. It returns the indexes of the sorted values. This is really useful if you want to use the sort order from one array to rearrange another array.
As suggested by #user1860611, unique seems to do what you want, using the third output as follows:
x = [ 13 15 5 5 0 0 0 1 0 3];
[~, ~, forwardRank] = unique(x);
%Returns
%forwardRank =
% 5 6 4 4 1 1 1 2 1 3
To get the order you want (decending) you'll need to reverse the order, like this:
reverseRank = max(forwardRank) - forwardRank + 1
%Returns
%reverseRank =
% 2 1 3 3 6 6 6 5 6 4
You may be done at this point. But you may want to sort these into the into an acsending order. This is a reorder of the reverseRank vector which keeping it in sync with the original x vector, which is exactly what the 2nd argument of sort is desined to help with. So we can do something like this:
[xSorted, ixsSort] = sort(x, 'descend'); %Perform a sort on x
reverseRankSorted = reverseRank(ixsSort); %Apply that sort to reverseRank
Which generates:
xSorted = 15 13 5 5 3 1 0 0 0 0
reverseRankSorted = 1 2 3 3 4 5 6 6 6 6
tiedrank.m might be the thing you are looking for.
>> x = round(rand(1,5)*10)
x =
8 7 3 10 0
>> tiedrank(x)
ans =
4 3 2 5 1
By default, MATLAB's sort function deals with ties/repeated elements by preserving the order of the elements, that is
>> [srt,idx] = sort([1 0 1])
srt =
0 1 1
idx =
2 1 3
Note that the two elements with value 1 in the input arbitrarily get assigned index 2 and 3, respectively. idx = [3 1 2], however, would be an equally valid sort.
I would like a function [srt,all_idx] = sort_ties(in) that explicitly returns all possible values for idx that are consistent with the sorted output. Of course this would only happen in the case of ties or repeated elements, and all_idx would be dimension nPossibleSorts x length(in).
I got started on a recursive algorithm for doing this, but quickly realized that things were getting out of hand and someone must have solved this before! Any suggestions?
I had a similar idea to what R. M. suggested. However, this solution is generalized to handle any number of repeated elements in the input vector. The code first sorts the input (using the function SORT), then loops over each unique value to generate all the permutations of the indices for that value (using the function PERMS), storing the results in a cell array. Then these index permutations for each individual value are combined into the total number of permutations for the sorted index by replicating them appropriately with the functions KRON and REPMAT:
function [srt,all_idx] = sort_ties(in,varargin)
[srt,idx] = sort(in,varargin{:});
uniqueValues = srt(logical([1 diff(srt)]));
nValues = numel(uniqueValues);
if nValues == numel(srt)
all_idx = idx;
return
end
permCell = cell(1,nValues);
for iValue = 1:nValues
valueIndex = idx(srt == uniqueValues(iValue));
if numel(valueIndex) == 1
permCell{iValue} = valueIndex;
else
permCell{iValue} = perms(valueIndex);
end
end
nPerms = cellfun('size',permCell,1);
for iValue = 1:nValues
N = prod(nPerms(1:iValue-1));
M = prod(nPerms(iValue+1:end));
permCell{iValue} = repmat(kron(permCell{iValue},ones(N,1)),M,1);
end
all_idx = [permCell{:}];
end
And here are some sample results:
>> [srt,all_idx] = sort_ties([0 2 1 2 2 1])
srt =
0 1 1 2 2 2
all_idx =
1 6 3 5 4 2
1 3 6 5 4 2
1 6 3 5 2 4
1 3 6 5 2 4
1 6 3 4 5 2
1 3 6 4 5 2
1 6 3 4 2 5
1 3 6 4 2 5
1 6 3 2 4 5
1 3 6 2 4 5
1 6 3 2 5 4
1 3 6 2 5 4
Consider the example A=[1,2,3,2,5,6,2]. You want to find the indices where 2 occurs, and get all possible permutations of those indices.
For the first step, use unique in combination with histc to find the repeated element and the indices where it occurs.
uniqA=unique(A);
B=histc(A,uniqA);
You get B=[1 3 1 1 1]. Now you know which value in uniqA is repeated and how many times. To get the indices,
repeatIndices=find(A==uniqA(B==max(B)));
which gives the indices as [2, 4, 7]. Lastly, for all possible permutations of these indices, use the perms function.
perms(repeatIndices)
ans =
7 4 2
7 2 4
4 7 2
4 2 7
2 4 7
2 7 4
I believe this does what you wanted. You can write a wrapper function around all this so that you have something compact like out=sort_ties(in). You probably should include a conditional around the repeatIndices line, so that if B is all ones, you don't proceed any further (i.e., there are no ties).
Here is a possible solution I believe to be correct, but it's somewhat inefficient because of the duplicates it generates initially. It's pretty neat otherwise, but I still suspect it can be done better.
function [srt,idx] = tie_sort(in,order)
L = length(in);
[srt,idx] = sort(in,order);
for j = 1:L-1 % for each position in sorted array, look for repeats following it
for k = j+1:L
% if repeat found, add possible permutations to the list of possible sorts
if srt(j) == srt(k)
swapped = 1:L; swapped(j) = k; swapped(k) = j;
add_idx = idx(:,swapped);
idx = cat(1,idx,add_idx);
idx = unique(idx,'rows'); % remove identical copies
else % because already sorted, know don't have to keep looking
break;
end
end
end