У меня есть 4 вектора столбца n-by-1, где совместное использование одного и того же номера индекса означает, что они имеют одну и ту же метку времени. Я хочу удалить «строки», которые идентичны их непосредственно предшествующим «строкам», и представить, что это выполняется рекурсивно до тех пор, пока не будет никаких изменений.
Например, предположим, что 4 вектора
C1=[1;1;3;3;1;1];
C2=[2;2;4;4;2;2];
C3=[0;0;0;0;0;0];
C4=[5;5;6;6;5;5];
Желаемый результат
ans=[1;3;5];
потому что [C1(ans),C2(ans),C3(ans),C4(ans)]
— это массив, в котором нет строки, идентичной предыдущей строке. В приведенном выше примере результирующие векторы выглядят так:
C1=[1;3;1];
C2=[2;4;2];
C3=[0;0;0];
C4=[5;6;5];
«Строки» как в строках, если смотреть на векторы, объединенные по столбцам с помощью [C1,C2,C3,C4]
.
Некоторые примечания:
Причина, по которой я начал с 4 разделенных векторов-столбцов, заключается в следующем:
У меня есть еще один вектор n на 1 с уникальными элементами, где я буду удалять одни и те же «строки» на основе индексов, удаленных для других 4 векторов;
в моем приложении данные извлекаются из другого места и сохраняются в типе данных Maltab поэлементно для дальнейшей обработки, и я получаю преимущество в производительности при сохранении в 4 двойных N на 1 по сравнению с 1 двойным N на 4. Это N исчисляется сотнями тысяч или миллионами.
n обычно составляет всего несколько тысяч за раз, но мне нужно минимизировать время, которое каждая фильтрация занимает как можно больше в течение 1 секунды и как можно меньше.
(Я хочу изучить методы, используя нативные функции, и сравнить производительность.)
Немного сложно продемонстрировать различия в производительности на этом, поскольку случайные данные не подходят, а слишком специфические данные не подходят. (Под трудным я подразумеваю, что это трудно сделать быстро.)
Но на случай, если кому-то интересно, с таблицей из ~ 164 тыс. строк и только ~ 1 тыс. «уникальных» строк («также вокруг строк») результаты timeit()
следующие.
Метод Криса diff or
: 0,0028 с.
Метод Вольфи unique
: 0,0142 с.
Метод Вольфи arrayfun
: 0,3912 с.
Метод Томаса diff*ones
: 0,0057 с.
Метод рекурсии Томаса: невозможно завершить. Это взорвало запрос ОЗУ Matlab до ~ 70 ГБ в течение минуты выполнения под timeit()
и вызвало зависание пользовательского интерфейса на моей машине с Win 10, несмотря на то, что на машине было много неиспользуемого процессора.
Цикл (но с varargin
по количеству столбцов): 3,6313 с.
Функции тестирования включали объединение, если не прямую обработку столбцов.
Версия цикла:
function varargout = accum(varargin)
for i=1:numel(varargin)
varargout{i}=varargin{i}(1); % assuming single column
end
for i=2:numel(varargin{1}) % assuming equal length
TF=false;
for j=1:numel(varargin)
TF=TF||varargin{j}(i)~=varargin{j}(i-1);
end
if TF
for j=1:numel(varargin)
varargout{j}=[varargout{j};varargin{j}(i)];
end
end
end
end
Если вы пишете другой ответ и вам нужны образцы данных, дайте мне знать. В противном случае я пропущу его вставку, не видя в этом особого смысла.
@obchardon: Да, я не контролирую источник данных. Со временем я просто буду использовать другого поставщика. Но мой нынешний источник работает до некоторой степени, и это просто недорого. Поэтому, чтобы начать работу, мне нужно использовать текущего поставщика, и в любом случае поставщик предлагает что-то ценное — дешевое — уникальным способом.
Вот вариант, использующий логические значения для подмножества строк в матрице
C([true; abs(C(2:end,:)-C(1:end-1,:))*ones(size(C,2),1)>0],:)
который дает
ans =
1 2 0 5
3 4 0 6
1 2 0 5
Если вы не возражаете против использования метода пользовательской функции, ниже может быть другой вариант, где myfun
рекурсивно вычисляет «уникальные» строки.
function y = myfun(x)
if size(x,1)==1
y = x;
else
v = x(end,:);
y = myfun(x(1:(end-1),:));
if ~all(y(end,:)==v)
y = [y;v];
end
end
end
такой, что
>> z = myfun(C)
z =
1 2 0 5
3 4 0 6
1 2 0 5
где C = [C1,C2,C3,C4]
Спасибо за ответ. Но unique(_,'rows')
не должно работать, потому что 1-й и 3-й элементы в примере могут быть одинаковыми, потому что могут быть промежуточные разные «ряды». Я отредактирую пример, чтобы показать его.
@Argyll Спасибо за ваш отзыв. Я обновил свой ответ, определив пользовательскую функцию. Надеюсь, это имеет смысл
Код имеет смысл. Спасибо за решение. Я опубликую заметку о тесте позже после получения дополнительных ответов.
@Argyll Я предполагаю, что пользовательская функция может работать медленно, если у вас много строк. Я добавил еще один метод, который должен быть намного быстрее.
Я думаю, что следующее дает желаемый результат (не проверено):
find([1; diff(C1) | diff(C2) | diff(C3) | diff(C4)])
diff
отличен от нуля, если два последующих элемента различны. Используя логическое ИЛИ, мы требуем, чтобы любой вектор имел разность в любой позиции. Первый элемент всегда является частью вывода. find
возвращает индексы ненулевых элементов.
Вы можете использовать аналогичный подход к ответу, данному Крисом (find(diff(...)))
), но сделать его более общим, используя unique
.
Настраивать:
C1=[1;1;3;3;1;1];
C2=[2;2;4;4;2;2];
C3=[0;0;0;0;0;0];
C4=[5;5;6;6;5;5];
C = [C1,C2,C3,C4];
Способ первый:
[~,~,iu] = unique( C, 'rows' );
idx = find( [1; diff(iu)] );
В качестве альтернативы вы можете выполнить цикл (сокращенно с помощью arrayfun
), чтобы найти строки, в которых любой элемент отличается от предыдущей строки.
Способ второй:
idx = find( [1, arrayfun( @(ii) any(C(ii,:) ~= C(ii-1,:)), 2:size(C,1) )] )
Просто вопрос, я предполагаю, что у вас нет контроля над исходными данными? Потому что в таком случае база данных в реальном времени (firebase, rethinkDB,...), которая может передавать данные в приложение, может значительно сократить объем необходимых вычислений.