расширить/заполнить матрицу Matlab

Я новичок в Matlab, поэтому мне нужно объяснение детских шагов.

У меня есть некоторые MIDI-данные, которые выглядят примерно так:

время включения/выключения note
10 1 61
90 0 61
90 1 72
92 1 87
100 0 72

Что я хочу сделать, так это расширить или «заполнить» пробелы, чтобы у меня была строка для каждого отдельного момента времени, и у меня были столбцы, которые показывают, какие заметки включены (часто одновременно есть более одной заметки). ).

конечная цель состоит в том, чтобы сделать некоторые расчеты общих отношений между нотами (гармонический диссонанс) в данный момент времени.

Поэтому я подумал, что, может быть, мне нужен новый столбец для каждой возможной ноты (их 127), а затем 1 или 0 для каждого раза. Или, может быть, я могу просто иметь матрицу, которая просто говорит мне, какие заметки включены (поэтому количество столбцов варьируется).

Я написал свой собственный псевдокод, но понятия не имею, как его реализовать. Я подозреваю, что есть простая функция, которая может это сделать. Вот мой псевдокод:

начать с 0, в момент времени 0 в новой "матрице примечаний"
для чисел: от 0 до n
если число совпадает с числом в столбце времени, перейти к столбцу включения/выключения для этой строки.
если 1 в столбце включения/выключения, то скопируйте число в столбце примечаний в «матрицу примечаний» для соответствующей строки
если 0, то не копируйте/ничего не делайте.

если число не совпадает с числом в столбце времени
, скопируйте предыдущую строку (которая может быть пустой, если не было примечаний).

для каждой строки в новой «матрице примечаний» расположите числа от младших к старшим в разных столбцах.

Так может кто подскажет что делать?? Я бьюсь головой о кирпичную стену здесь!


person Tom    schedule 27.03.2011    source источник


Ответы (1)


Вот решение, которое будет работать, даже если список находится в совершенно случайном порядке. Он основан на следующей идее: кумулятивная сумма вектора [0 1 0 0 -1 0 0] равна [0 1 1 1 0 0 0]. Это соответствует «включено» во время 2 и «выключено» во время 5. Теперь все, что нам нужно сделать, это заполнить массив 1 и -1 и запустить CUMSUM, чтобы преобразовать его в массив, в каждом столбце которого есть единицы, когда звук равен on.

Я предполагаю, что есть 128 нот (0-127), и что вы хотите иметь один временной шаг тишины (если все ноты в конечном итоге заканчиваются) в самом конце. Обратите внимание, что Matlab начинает отсчет с 1, поэтому время 0 соответствует строке №1.

%# midiData is a n-by-3 array with [time,on/off,note]
midiData = [...
10 1 61
90 0 61
90 1 72
92 1 87
100 0 72];

%# do not call unique here, because repeated input rows are relevant

%# note values can be from 0 to 127
nNotes = 128;

%# nTimepoints: find the highest entry in midiData's time-column
%# add 2, because midiData starts counting time at 0
%# and since we want to have one additional timepoint in the end
nTimepoints = max(midiData(:,1))+2; 




%# -- new solution ---
%# because the input is a bit messed up, we have to use a more complicated
%# solution. We'll use `accumarray`, with which we sum up all the
%# entries for on (+1) and off (-1) for each row(time)/column(note) pair.
%# after that, we'll apply cumsum

%# transform the input, so that 'off' is -1
%# wherever the second col of midiData is 0
%# replace it with -1
midiData(midiData(:,2)==0,2) = -1;

%# create output in one step
%# for every same coordinate (time,note), sum all the 
%# on/offs (@sum). Force the output to be 
%# a nTimepoints-by-nNotes array, and fill in zeros
%# where there's no information
output = accumarray(midiData(:,[1 3])+1,midiData(:,2),...
    [nTimepoints,nNotes],@sum,0);

%# cumsum, and we're done
output = cumsum(output,1);

Предыдущее решение для полноты:

%# --- old solution ---

 %# create output array, which we'll first populate with 1 and -1
%# after which we transform it into an on-off array
%# rows are timepoints, columns are notes
output = zeros(nTimepoints,nNotes);

%# find all on's 
%# onIdx is 1 if the second column of midiData is 1
onIdx = midiData(:,2) == 1;

%# convert time,note pairs into linear indices for
%# writing into output in one step
%# Add 1 to time and note, respectively, so that we start counting at 1
plusOneIdx = sub2ind([nTimepoints,nNotes],midiData(onIdx,1)+1,midiData(onIdx,3)+1);

%# write "1" wherever a note turns on
output(plusOneIdx) = 1;

%# now do the same for -1
offIdx = midiData(:,2) == 0;
minusOneIdx = sub2ind([nTimepoints,nNotes],midiData(offIdx,1)+1,midiData(offIdx,3)+1);

%# instead of overwrite the value in output, subtract 1
%# so that time/note that are both on and off become zeros
output(minusOneIdx) = output(minusOneIdx) - 1;

%# run cumsum on the array to transform the +1/-1 into stretches of 1 and 0
%# the 'dim' argument is 1, because we want to sum in the direction in 
%# which rows are counted
output = cumsum(output,1);

%# for fun, visualize the result
%# there's white whenever a note is on
imshow(output)
person Jonas    schedule 27.03.2011
comment
очень интересно, спасибо! но как мне увидеть результат как новую матрицу, чтобы я мог ее проверить? Визуализация вывода в моем реальном MIDI-файле говорит, что он слишком большой (88000 строк). - person Tom; 27.03.2011
comment
Другими словами, есть ли способ просмотреть этот вывод в виде файла Excel? Кроме того, из того, что я могу разобрать на выходе Matlab, у меня иногда получается -2 или -1? Я не уверен, что это значит. - person Tom; 27.03.2011
comment
@Tom: единственная причина, по которой output может содержать отрицательные значения, заключается в том, что (1) есть остановка без начала или (2) есть повторяющиеся значения для стартов и остановок - см. Мое редактирование, чтобы исправить № 2. Кроме того, для записи в Excel используйте xlswrite('myFile.xls',output) - person Jonas; 27.03.2011
comment
Я также заметил кое-что еще странное - в этом виноваты данные. Когда нота повторяется, MIDI-файл показывает 0 для ноты в это время, а затем 1 для того же самого времени, той же ноты несколькими строками позже. Я думаю, что это запутывает сценарий. На предварительном просмотре вы, кажется, предвидели проблему! какой бит скрипта изменен? - person Tom; 27.03.2011
comment
@Tom: Да, это проблема для кода. Однако назначение «выкл.» перед назначением «вкл.» должно решить проблему (я не буду исправлять это в коде, так как не хочу перепечатывать все комментарии). - person Jonas; 27.03.2011
comment
@ Джонас. Привет еще раз. Я пытался реализовать ваш сценарий. Я попытался просто поменять местами порядок включения и выключения частей скрипта, что, казалось, решило старую проблему, но теперь я накапливаю положительные числа в столбцах. Извините, я, наверное, туплю об этом! - person Tom; 28.03.2011
comment
@Tom: О, теперь я понимаю твои входные данные. Хорошо, это легко исправить: добавьте вместо замены значения. Посмотрите мое редактирование, где я добавляю минусовые (кстати, больше не нужно менять местами части кода). - person Jonas; 28.03.2011
comment
@йонас. Еще раз спасибо, но должно ли дубликат времени/нот быть 1 или 0, зависит от порядка, в котором они представлены в файле - истинное значение должно быть более поздним... На самом деле держите это, оно всегда будет 1, потому что оно никогда не говорит и тут же выключается. - person Tom; 28.03.2011
comment
@Tom: Да, но последовательность 1,0/1,0/1,0/1,0 должна стать 1,0,0,0,-1 перед суммой, чтобы в итоге получилось 1,1, 1,1,0 в конце. Вот что сделает дополнение. - person Jonas; 28.03.2011
comment
хорошо, круто. Мне нужно будет проверить это через несколько минут, но похоже, отличное решение! - person Tom; 28.03.2011
comment
@йонас. и снова здравствуйте. Я обнаружил проблему, когда у меня есть такие данные. Сценарий делает строку уникальной, поэтому удаляет 1 из «включенных» строк. Но это оставляет строку «выкл» или 0 в качестве первой точки, поэтому возникают ошибки с отрицательным числом. Есть ли способ исправить это? 384...1...71 384...0...71 384...1...71 - person Tom; 28.03.2011
comment
в «уникальном» файле справки это указывает, что добавление «первого» может решить проблему, но я, похоже, не могу заставить его работать. - person Tom; 28.03.2011
comment
на самом деле, если будет удалена только последняя строка, окончательное значение останется равным 0, так что это все равно не сработает - мне нужно избавиться от обеих избыточных строк. - person Tom; 28.03.2011
comment
@Tom: на самом деле порядок строк не проблема для моего решения. Однако 1..0..1 должен оцениваться как 1, а с unique он станет 1..0 и, таким образом, оценивается как 0. - person Jonas; 28.03.2011
comment
@йонас. Извините, это не работает. Нет ли какого-нибудь цикла, который я мог бы запустить, чтобы просто удалить оскорбительные строки? Я попытался умножить столбец 1 на столбец 3, а затем запустить цикл, так что если результат соответствует другому результату минус 2 строки, он удалит строки, но он просто продолжает жаловаться. Помогите, пожалуйста! - person Tom; 28.03.2011
comment
@Tom: я обновил решение для использования accumarray, так что теперь оно должно работать. - person Jonas; 28.03.2011