简体   繁体   中英

Matlab matrix and vector sorting for indexes

I have a matrix whose rows represent music notes. [A,A#,B,C,C#,D,D#,E,F,F#,G,G#] So index 1 represents A, 2 represents A# and so on. In the matrix, the largest the element in that index the more likely it is to be present in the audio.

I want to calculate which 3 indexes are appearing most frequently between the possible 12.

This is how I reasoned it out. First I sort every column using [B,I] = sort().. B now contains the sorted column I now contains the indexes sorted in an ascending manner.

Therefore I[10], I[11] and I[12] now contain the 3 largest indexes in that column.

Now I create a new vector called notes. If a particular note when sorted, finds itself among the largest 3 indexes of I, then the respective note should be incremented.

I'm trying for the first 3 columns, which are these:

0.0690   0.0530   0.0656
0.2453   0.2277   0.2306
0.0647   0.0315   0.0494
1.2037   1.1612   1.1613
0.0772   0.0346   0.0367
0.1628   0.1429   0.1648
0.0572   0.0370   0.0493
0.4119   0.3577   0.3635
0.0392   0.0430   0.0466
0.1182   0.0921   0.0935
0.7473   0.6680   0.7088
0.0794   0.0527   0.0566

Hence in B for the first loop iteration I should get,

 0.0392
 0.0572
 0.0674
 0.0690
 0.0772
 0.0794
 0.1182
 0.1628
 0.2453
 0.4119
 0.7473
 1.2037

which I'm getting (so till now everything's ok). I (which contains the sorted indexes) is being returned correctly too with the 3 largest indexes being 8, 11, 4 (ie. the original indexes that contained the largest element where 8, 11, 4 in ascending order)

The problem is within the if condition. In the first iteration, After the if condition, the 'notes' column vector should be incremented in the 8th place, 11th place, 4th place ie.

0 
0 
0 
1 
0 
0 
0 
1 
0 
0 
1 
0 

After the if condition however, only the 4th place in the vector is being incremented. In fact after first iteration, when displaying the 'notes' vector, I am getting

 0
 0
 0
 1
 0
 0
 0
 0
 0
 0
 0
 0

And this is the code:

for col = 1:3        
    [B,I] = sort(C(:,col), 'ascend'); %B is the sorted column. I is the sorted indexes
    fprintf('Col No:');
    disp(col);
    fprintf('Sorted Column: ');
    disp(B);
    fprintf('Sorted Indexes: ');
    disp(I);

    if (I(10) == 1 || I(11) == 1 || I(12) == 1)
        notes(1,:) = notes(1,:) + 1;
    elseif (I(10) == 2 || I(11) == 2 || I(12) == 2)
        notes(2,:) = notes(2,:) + 1;
    elseif (I(10) == 3 || I(11) == 3 || I(12) == 3)
        notes(3,:) = notes(3,:) + 1;
    elseif (I(10) == 4 || I(11) == 4 || I(12) == 4)
        notes(4,:) = notes(4,:) + 1;
    elseif (I(10) == 5 || I(11) == 5 || I(12) == 5)
        notes(5,:) = notes(5,:) + 1;
    elseif (I(10) == 6 || I(11) == 6 || I(12) == 6)
        notes(6,:) = notes(6,:) + 1;
    elseif (I(10) == 7 || I(11) == 7 || I(12) == 7)
        notes(7,:) = notes(7,:) + 1;
    elseif (I(10) == 8 || I(11) == 8 || I(12) == 8)
        notes(8,:) = notes(8,:) + 1;
    elseif (I(10) == 9 || I(11) == 9 || I(12) == 9)
        notes(9,:) = notes(9,:) + 1;
    elseif (I(10) == 10 || I(11) == 10 || I(12) == 10)
        notes(10,:) = notes(10,:) + 1;
    elseif (I(10) == 11 || I(11) == 11 || I(12) == 11)
        notes(11,:) = notes(11,:) + 1;
    elseif (I(10) == 12 || I(11) == 12 || I(12) == 12)
        notes(12,:) = notes(12,:) + 1;

    end

    disp(notes);

What am I doing wrong? The code may be wrong (or perhaps it could be better). I am in no means good at Matlab. This is the first time using it.

Would appreciate your ideas, opinions, corrections.

Thank you for your time in advance

You seem to be overthinking the problem. What you can do is use sort and independently apply the sorting to each column individually. The sort function uses a second parameter that tells you which dimension you want to apply the sorting. As you want to sort over the columns, you need to set the second parameter to 1, meaning that you want to sort the rows for each column.

Therefore, defining your matrix of "notes" C :

C = [0.0690   0.0530   0.0656
0.2453   0.2277   0.2306
0.0647   0.0315   0.0494
1.2037   1.1612   1.1613
0.0772   0.0346   0.0367
0.1628   0.1429   0.1648
0.0572   0.0370   0.0493
0.4119   0.3577   0.3635
0.0392   0.0430   0.0466
0.1182   0.0921   0.0935
0.7473   0.6680   0.7088
0.0794   0.0527   0.0566];

You can simply do this:

[~,I] = sort(C, 1, 'descend')

The 'descend ' flag sorts the values in descending order. As such, I would be a matrix where each column tells you where the positions of each sorted values would appear per column. Therefore, you would simply get the first three rows of I .

As such, if we display the first three rows of I , we get:

>> disp(I(1:3,:))

 4     4     4
11    11    11
 8     8     8

This makes sense, as for each column, we found the highest values at positions 4, 11 and 8 respectively.

Now, if you want to tally up those positions that you see in the above matrix, you can very simply use either histc or accumarray .

Therefore:

ind = I(1:3,:); %// Get the first three rows of the sorted indices
notes = histc(ind(:), 1:12); %// or
%notes = accumarray(ind(:), 1, [12 1]);

As such, for notes we get:

notes =

     0
     0
     0
     3
     0
     0
     0
     3
     0
     0
     3
     0

As such, we see indices 4, 8 and 11 for each column, and so we should increment these positions by 1 each time. histc counts up the frequency of things that occur, and so we can naturally use this and tally up the number of times we see 4, 8 and 11, or whatever we see for the first three rows of I . accumarray does the same thing, but accumarray is a more general function that does more than just add up things in the same bins together. histc is the better way to go for your case - especially since the number of bins is constant.


As an edit to this post, below in your comments you said you'd like to be able to apply this for 18 column segments. The best thing would probably be to do this in some sort of loop. First figure out how many segments of 18 columns we have, and then put this in a loop for that many times and update our results. At each iteration in the loop, we need to pull out the right 18 columns in the matrix, and keep getting chunks of 18 until we hit the end of the array.

We'd place our results in another matrix where each column is the result of the three most frequently occurring notes per 18 column segment.

Something like this:

numColumns = size(C, 2); %// Get total number of columns for matrix
numSegments = ceil(numColumns / 18); %// Get total number of segments

finalMatrix = zeros(12, numSegments);
for idx = 1 : numSegments

    %// Make sure that when we get 18 chunks, we don't out of bounds
    if 18*idx > numColumns
        seg = C(:,(idx-1)*18 + 1 : end);
    else
        seg = C(:,(idx-1)*18 + 1 : 18*idx);
    end

    [~,I] = sort(seg, 1, 'descend');
    ind = I(1:3,:); %// Get the first three rows of the sorted indices
    notes = histc(ind(:), 1:12);

    %// Your code
    [~,y] = sort(notes, 'descend'); 
    finalvec = zeros(12,1);
    finalvec(y(1)) = 1; finalvec(y(2)) = 1; finalvec(y(3)) = 1;

    %// Place in final matrix
    finalMatrix(:, idx) = finalvec;
end

finalMatrix will store the results of each 18 piece segment into a single column, where each column is the logic you wrote in your comment.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM