Say I have this vector A:
A=[2
92
91
91
91
92
9
92
-1
91];
I want to write a code to rename the smallest entry as 1, the next smallest entry as 2, and so on. So, I want the output to be:
B=[2
5
4
4
4
5
3
5
1
4];
How do I do that with a short and efficient code? The code I have been able to write is a "check one by one and rename" kind of code, which is highly inefficient.
U=unique(A);
for a=1:size(U,1)
for b=1:size(A,1)
if A(b,1)==U(a,1)
B(b,1)=a;
end
end
end
Is it possible to write one without using for loops, or one that is otherwise efficient?
As a consequence of unique sorting the output, your desired array is automatically built by the function and is accessible via the third output:
>> A=[2;92;91;91;91;92;9;92;-1;91];
>> [~,~,B] = unique(A)
B =
2
5
4
4
4
5
3
5
1
4
Related
I have been given this problem in a MATLAB course I am doing. The instructor's solution provided there is wrong, and I have been struggling with the same problem for hours as I am a beginner who has just started coding (a science student here).
Consider a one-dimensional matrix A such as A = \[5 8 8 8 9 9 6 6 5 5 4 1 2 3 5 3 3 \]. Show the percentage frequencies of unique
elements in the matrix A in descending order.
Hint: Use the functions tabulate and sort.
How do I solve this problem using only tabulate, sort, and find functions (find is for eliminating zero frequency elements in tabulate table, which my instructor did not do)?
I tried first extracting the indices of non-zero elements in the percentage column of tabulating table using the find function, which I succeeded in doing using the following:
A = [5 8 8 8 9 9 6 6 5 5 4 1 2 3 5 3 3 ];
B = tabulate(A);
C = find(B(:,3) > 0)
But I am now struggling to return the values corresponding to the 3rd column of B using indices in C. Please help. Also please give me some alternative syntax where one can easily make a vector out of non-zero elements of a row or column easily by omitting the zeroes in that vector if it exists. Rest of the problem I'll do by myself.
With your find command, you are just finding the indices of the matrix and not the values themselves.
So you either will do something like this:
A = [5 8 8 8 9 9 6 6 5 5 4 1 2 3 5 3 3 ];
B = tabulate(A);
for i = 1:size(B,1)-1
if B(i,3) == 0
B(i,:) = [];
end
end
sortrows(B,3,'descend')
where you remove the 0 value's row.
Or since you have all the numbers with none-zero frequency you can ask for their rows. Like this:
A = [5 8 8 8 9 9 6 6 5 5 4 1 2 3 5 3 3 ];
B = tabulate(A);
C = find(B(:,3) > 0);
sortrows(B(C(:),:),3,'descend')
in a bit more elegant way. B(C(:),:) calls all the rows with first indices the indices of matrix C. Which is exactly what you are asking for. While at the same time you sort your matrix based on row 3 at a descending order.
How do I change the list of value to all 1? I need the top right to bottom left also end up with 1.
rc = input('Please enter a value for rc: ');
mat = ones(rc,rc);
for i = 1:rc
for j = 1:rc
mat(i,j) = (i-1)+(j-1);
end
end
final = mat
final(diag(final)) = 1 % this won't work?
Code for the original problem -
final(1:size(final,1)+1:end)=1
Explanation: As an example consider a 5x5 final matrix, the diagonal elements would have indices as (1,1), (2,2) .. (5,5). Convert these to linear indices - 1, 7 and so on till the very last element, which is exactly what 1:size(final,1)+1:end gets us.
Edit : If you would like to set the diagonal(from top right to bottom left elements) as 1, one approach would be -
final(fliplr(eye(size(final)))==1)=1
Explanation: In this case as well we can use linear indexing, but just for more readability and maybe a little fun, we can use logical indexing with a proper mask, which is being created with fliplr(eye(size(final)))==1.
But, if you care about performance, you can use linear indexing here as well, like this -
final(sub2ind(size(final),1:size(final,1),size(final,2):-1:1))=1
Explanation: Here we are creating the linear indices with the rows and columns indices of the elements to be set. The rows here would be - 1:size(final,1) and columns are size(final,2):-1:1. We feed these two to sub2ind to get us the linear indices that we can use to index into final and set them to 1.
If you would to squeeze out the max performance here, go with this raw version of sub2ind -
final([size(final,2)-1:-1:0]*size(final,1) + [1:size(final,1)])=1
All of the approaches specified so far are great methods for doing what you're asking.
However, I'd like to provide another viewpoint and something that I've noticed in your code, as well as an interesting property of this matrix that may or may not have been noticed. All of the anti-diagonal values in your matrix have values equal to rc - 1.
As such, if you want to set all of the anti-diagonal values to 1, you can cheat and simply find those values equal to rc-1 and set these to 1. In other words:
final(final == rc-1) = 1;
Minor note on efficiency
As a means of efficiency, you can do the same thing your two for loops are doing when constructing mat by using the hankel command:
mat = hankel(0:rc-1,rc-1:2*(rc-1))
How hankel works in this case is that the first row of the matrix is specified by the vector of 0:rc-1. After, each row that follows incrementally shifts values to the left and adds an increasing value of 1 to the right. This keeps going until you encounter the vector seen in the second argument, and at this point we stop. In other words, if we did:
mat = hankel(0:3,3:6)
This is what we get:
mat =
0 1 2 3
1 2 3 4
2 3 4 5
3 4 5 6
Therefore, by specifying rc = 5, this is the matrix I get with hankel, which is identical to what your code produces (before setting the anti-diagonal to 1):
mat =
0 1 2 3 4
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
Tying it all together
With hankel and the cheat that I mentioned, we can compute what you are asking in three lines of code - with the first line of code asking for the dimensions of the matrix:
rc = input('Please enter a value for rc: ');
mat = hankel(0:rc-1, rc-1:2*(rc-1));
mat(mat == rc-1) = 1;
mat contains your final matrix. Therefore, with rc = 5, this is the matrix I get:
mat =
0 1 2 3 1
1 2 3 1 5
2 3 1 5 6
3 1 5 6 7
1 5 6 7 8
Here's a simple method where I just add/subtract the appropriate matrices to end up with the right thing:
final=mat-diag(diag(mat-1))+fliplr(diag([2-rc zeros(1,rc-2) 2-rc]))
Here is one way to do it:
Say we have a the square matrix:
a = ones(5, 5)*5
a =
5 5 5 5 5
5 5 5 5 5
5 5 5 5 5
5 5 5 5 5
5 5 5 5 5
You can remove the diagonal, then create a diagonal list of ones to replace it:
a = a - fliplr(diag(diag(fliplr(a)))) + fliplr(diag(ones(length(a), 1)))
a =
5 5 5 5 1
5 5 5 1 5
5 5 1 5 5
5 1 5 5 5
1 5 5 5 5
The diag(ones(length(a), 1)) can be any vector, ie. 1->5:
a = a - fliplr(diag(diag(fliplr(a)))) + fliplr(diag(1:length(a)))
a =
5 5 5 5 1
5 5 5 2 5
5 5 3 5 5
5 4 5 5 5
5 5 5 5 5
a=[2 3 6 7 2 1 0.01 6 8 10 12 15 18 9 6 5 4 2].
Here is an array i need to extract the exact values where the increasing and decreasing trend starts.
the output for the array a will be [2(first element) 2 6 9]
a=[2 3 6 7 2 1 0.01 6 8 10 12 15 18 9 6 5 4 2].
^ ^ ^ ^
| | | |
Kindly help me to get the result in MATLAB for any similar type of array..
You just have to find where the sign of the difference between consecutive numbers changes.
With some common sense and the functions diff, sign and find, you get this solution:
a = [2 3 6 7 2 1 0.01 6 8 10 12 15 18 9 6 5 4 2];
sda = sign(diff(a));
idx = [1 find(sda(1:end-1)~=sda(2:end))+2 ];
result = a(idx);
EDIT:
The sign function messes things up when there are two consecutive numbers which are the same, because sign(0) = 0, which is falsely identified as a trend change. You'd have to filter these out. You can do this by first removing the consecutive duplicates from the original data. Since you only want the values where the trend change starts, and not the position where it actually starts, this is easiest:
a(diff(a)==0) = [];
This is a great place to use the diff function.
Your first step will be to do the following:
B = [0 diff(a)]
The reason we add the 0 there is to keep the matrix the same length because of the way the diff function works. It will start with the first element in the matrix and then report the difference between that and the next element. There's no leading element before the first one so is just truncates the matrix by one element. We add a zero because there is no change there as it's the starting element.
If you look at the results in B now it is quite obvious where the inflection points are (where you go from positive to negative numbers).
To pull this out programatically there are a number of things you can do. I tend to use a little multiplication and the find command.
Result = find(B(1:end-1).*B(2:end)<0)
This will return the index where you are on the cusp of the inflection. In this case it will be:
ans =
4 7 13
I am wondering whether I can do the following efficiently in Matlab. Writing a naive loop for the problem is pretty straightforward, but I am trying to find whether there are any specialized functions one could use (perhaps arrayfun / accumarray (?) -- both of which I have great trouble in understanding!) Thanks in advance.
Let's say I have two vectors as follows (in Matlab):
A = [15 4 9 6 7 5 11 3 14];
B = [2 7 13];
I would like to do the following:
Sort B, if not sorted already.
For every successive windows in B (i.e., [2,7], [7,13]), find the corresponding elements in A that lie within the window.
In this "partitioned" A, decrement by 1 n-times from those elements in A that lie in the n-th window of B.
Example: In the above case, the first window of B is [2,7]. The elements in A that lie within this window are [5,4,3,6]. As they lie within the first window of B, I need to decrement 1, one time from each of these elements. The new A will look like the following after this operation:
A = [15 3 9 5 7 4 11 2 14];
Can this problem be reduced to a few function calls in Matlab or one should go through the naive loop business anyway? Thanks!
This can be done quite easily by using the histc function to determine in what bin (what you called "window") the values are.
A = [15 4 9 6 7 5 11 3 14];
B = [2 7 13];
B = sort(B);
[~, bin] = histc(A, B);
A = A - bin;
edit:
I noticed my solution is different from yours, but I suspect you made a mistake in your calculation. Do you have to subtract 2 from the values within the second bin or do you leave them as-is? If you only want to change the values in the first bin, the last line should read A(bin==1) = A(bin==1) - 1.
A = [15 4 9 6 7 5 11 3 14]; % initial value of A
A = [15 3 9 5 7 4 11 2 14]; % your reference result
A = [15 3 7 5 5 4 9 2 14]; % my result (as above)
A = [15 3 9 5 7 4 11 2 14]; % my result with A(bin==1) = A(bin==1) - 1
To change in what bin a value on the edge of a bin should end up, you can try to add/subtract eps from B.
ok it seems like a simple problem, but i am having problem
I have a timer for each data set which resets improperly and as a result my timing gets mixed.
Any ideas to correct it? without losing any data.
Example
timer col ideally should be
timer , mine reads
1 3
2 4
3 5
4 6
5 1
6 2
how do i change the colum 2 or make a new colum which reads like colum 1 without changing the order of ther rows which have data
this is just a example as my file lengths are 86000 long , also i have missing timers which i do not want to miss , this imples no data for that period of time.
thanks
EDIT: I do not want to change the other columns. The coulm 1 is the gps counter and so it does not sync with the comp timer due to some other issues. I just want to change the row one such that it goes from high to low without effecting other rows. also take care of missing pts ( if i did not care for missing pts simple n=1: max would work.
missing data in this case is indicated by missing timer. for example i have 4,5,8,9 with missing 6,7
Ok let me try to edit agian
its a 8600x 80 matrix of data:
timer is one row which should go from 0 to 8600
but timer starts at odd times , so i have start of data from middle , lets say 3400, so in the middle of day my timer goes to 0 and then back to 1.
but my other rows are fine. I just need 2 plot other sets based on timer as time.
i cannot use T= 1:length(file) as then it ignores missed time stamps ( timers )
for example my data reads like
timer , mine reads
1 3
2 4
3 5
4 8
5 9
8 1
9 2
so u can see time stamps 6,7 are missing.
if i used n=1:length(file)
i would have got
1 2 3 4 5 6 7
which is wrong
i want
1 2 3 4 5 8 9
without changing the order of other rows , so i cannot use sort for the whole file.
I assume the following problem
data says
3 100
4 101
5 102
NaN 0
1 104
2 105
You want
1 100
2 101
3 102
NaN 0
4 104
5 105
I'd solve the problem like this:
%# create test data
data = [3 100
4 101
5 102
NaN 0
1 104
2 105];
%# find good rows (if missing data are indicated by zeros, use
%# goodRows = data(:,1) > 0;
goodRows = isfinite(data(:,1));
%# count good rows
nGoodRows = sum(goodRows);
%# replace the first column with sequential numbers, but only in good rows
data(goodRows,1) = 1:nGoodRows;
data =
1 100
2 101
3 102
NaN 0
4 104
5 105
EDIT 1
Maybe I understand your question this time
data says
4 101
5 102
1 104
2 105
You want
1 4 101
2 5 102
4 1 104
5 2 105
This can be achieved the following way
%# test data
data = [4 101
5 102
1 104
2 105];
%# use sort to get the correct order of the numbers and add it to the left of data
out = [sort(data(:,1)),data]
out =
1 4 101
2 5 102
4 1 104
5 2 105
EDIT 2
Note that out is the result from the solution in EDIT 1
It seems you want to plot the data so that there is no entry for missing values. One way to do this is to make a plot with dots - there won't be a dot for missing data.
plot(out(:,1),out(:,3),'.')
If you want to plot a line that is interrupted, you have to insert NaNs into out
%# create outNaN, that has NaN-rows for missing entries
outNaN = NaN(max(out(:,1)),size(out,2));
outNaN(out(:,1),:) = out;
%# plot
plot(out(:,1),out(:,3))