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

У меня есть 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].

Вопрос:

  • Я понимаю, как это сделать с помощью цикла. Как вы делаете это с помощью собственных функций Matlab?

Некоторые примечания:

Причина, по которой я начал с 4 разделенных векторов-столбцов, заключается в следующем:

  1. У меня есть еще один вектор n на 1 с уникальными элементами, где я буду удалять одни и те же «строки» на основе индексов, удаленных для других 4 векторов;

  2. в моем приложении данные извлекаются из другого места и сохраняются в типе данных 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

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

Просто вопрос, я предполагаю, что у вас нет контроля над исходными данными? Потому что в таком случае база данных в реальном времени (firebase, rethinkDB,...), которая может передавать данные в приложение, может значительно сократить объем необходимых вычислений.

obchardon 21.12.2020 10:30

@obchardon: Да, я не контролирую источник данных. Со временем я просто буду использовать другого поставщика. Но мой нынешний источник работает до некоторой степени, и это просто недорого. Поэтому, чтобы начать работу, мне нужно использовать текущего поставщика, и в любом случае поставщик предлагает что-то ценное — дешевое — уникальным способом.

Argyll 02.01.2021 15:18
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
126
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вот вариант, использующий логические значения для подмножества строк в матрице

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 20.12.2020 21:24

@Argyll Спасибо за ваш отзыв. Я обновил свой ответ, определив пользовательскую функцию. Надеюсь, это имеет смысл

ThomasIsCoding 20.12.2020 21:58

Код имеет смысл. Спасибо за решение. Я опубликую заметку о тесте позже после получения дополнительных ответов.

Argyll 20.12.2020 22:40

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

ThomasIsCoding 21.12.2020 00:43
Ответ принят как подходящий

Я думаю, что следующее дает желаемый результат (не проверено):

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) )] )

Другие вопросы по теме