Подтвердить что ты не робот

Возвращение уникального элемента с допуском

В Matlab есть команда unique, которая возвращает уникальные строки в массиве. Это очень удобная команда.

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

4b9b3361

Ответ 1

С R2015a этот вопрос, наконец, имеет простой ответ (см. мой другой ответ на этот вопрос). Для релизов до R2015a существует такая встроенная (недокументированная) функция: _mergesimpts. Безопасная догадка о композиции имени - "слить похожие точки".

Вызывается функция со следующим синтаксисом:

xMerged = builtin('_mergesimpts',x,tol,[type])

Массив данных x равен N-by-D, где N - количество точек, а D - количество измерений. Допуски для каждого измерения определяются вектором строки D -element, tol. Необязательный входной аргумент type - это строка ('first' (по умолчанию) или 'average'), указывающая, как слить похожие элементы.

Выход xMerged будет M-by-D, где M<=N. Он сортируется.

Примеры, 1D данные:

>> x = [1; 1.1; 1.05];             % elements need not be sorted
>> builtin('_mergesimpts',x,eps)   % but the output is sorted
ans =
    1.0000
    1.0500
    1.1000

Типы слияния:

>> builtin('_mergesimpts',x,0.1,'first')
ans =
    1.0000  % first of [1, 1.05] since abs(1 - 1.05) < 0.1
    1.1000
>> builtin('_mergesimpts',x,0.1,'average')
ans =
    1.0250  % average of [1, 1.05]
    1.1000
>> builtin('_mergesimpts',x,0.2,'average')
ans =
    1.0500  % average of [1, 1.1, 1.05]

Примеры, 2D-данные:

>> x = [1 2; 1.06 2; 1.1 2; 1.1 2.03]
x =
    1.0000    2.0000
    1.0600    2.0000
    1.1000    2.0000
    1.1000    2.0300

Все двумерные точки, уникальные для точности машины:

>> xMerged = builtin('_mergesimpts',x,[eps eps],'first')
xMerged =
    1.0000    2.0000
    1.0600    2.0000
    1.1000    2.0000
    1.1000    2.0300

Объединить на основе допусков второго измерения:

>> xMerged = builtin('_mergesimpts',x,[eps 0.1],'first')
xMerged =
    1.0000    2.0000
    1.0600    2.0000
    1.1000    2.0000   % first of rows 3 and 4
>> xMerged = builtin('_mergesimpts',x,[eps 0.1],'average')
xMerged =
    1.0000    2.0000
    1.0600    2.0000
    1.1000    2.0150   % average of rows 3 and 4

Объединить на основе допусков первого измерения:

>> xMerged = builtin('_mergesimpts',x,[0.2 eps],'average')
xMerged =
    1.0533    2.0000   % average of rows 1 to 3
    1.1000    2.0300
>> xMerged = builtin('_mergesimpts',x,[0.05 eps],'average')
xMerged =
    1.0000    2.0000
    1.0800    2.0000   % average of rows 2 and 3
    1.1000    2.0300   % row 4 not merged because of second dimension

Объединить на основе обоих размеров:

>> xMerged = builtin('_mergesimpts',x,[0.05 .1],'average')
xMerged =
    1.0000    2.0000
    1.0867    2.0100   % average of rows 2 to 4

Ответ 2

Это сложная проблема. Я даже утверждал, что это невозможно решить в общем, из-за того, что я назвал бы проблемой транзитивности. Предположим, что мы имеем три элемента в множестве {A, B, C}. Я определю простую функцию isSimilarTo, такую, что isSimilarTo (A, B) вернет истинный результат, если два входа находятся в пределах определенного допуска друг к другу. (Обратите внимание, что все, что я скажу здесь, имеет смысл как в одном измерении, так и в нескольких измерениях.) Поэтому, если известно, что два числа "похожи" друг на друга, мы будем выбирать их вместе.

Итак, предположим, что мы имеем значения {A, B, C} такие, что isSimilarTo (A, B) истинно, и это похоже на (B, C) также верно. Должны ли мы решить сгруппировать все три вместе, хотя isSimilarTo (A, C) является ложным?

Хуже, переходим к двум измерениям. Начните с k точек, равномерно расположенных по периметру круга. Предположим, что допуски выбраны так, что любая точка находится в пределах определенного допуска своих непосредственных соседей, но не в какой-либо другой точке. Как вы решите решить, какие точки являются "уникальными" в настройке?

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

Сказав это, я все же предлагаю компромисс, то, что иногда может работать в пределах. Этот трюк находится в Консолидаторе, как показано в обмене файлами Matlab Central. Мой подход состоял в том, чтобы эффективно объединить входные данные с точностью до указанного допуска. Сделав это, комбинация уникального и аккумулирующего позволяет эффективно выполнять агрегацию даже для больших наборов данных в одном или многих измерениях.

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

Ответ 3

По состоянию на R2015a, наконец, есть функция для этого, uniquetol (до R2015a, см. мой другой ответ):

uniquetol Установите уникальное значение в пределах допуска.

    uniquetol аналогичен unique. В то время как unique выполняет точные сравнения, uniquetol выполняет сравнения с использованием допуска.

Синтаксис прост:

C = uniquetol(A,TOL) возвращает уникальные значения в A с использованием допуска TOL.

Как и семантика:

Каждое значение C находится в пределах допуска одного значения A, но ни один из двух элементов в C не находится в пределах допуска друг к другу. C сортируется в порядке возрастания. Два значения u и v находятся в пределах допуска, если:
    abs(u-v) <= TOL*max(A(:),[],1)

Он также может работать "ByRows", и допуск можно масштабировать с помощью ввода "DataScale", а не максимального значения входных данных.

Но есть важное замечание об уникальности решений:

Может быть несколько допустимых выходов C, которые удовлетворяют условию: "никакие два элемента в C не допускают друг друга". Например, замена столбцов в A может привести к возврату другого решения, потому что вход сортируется лексикографически столбцами. Другим результатом является то, что uniquetol(-A,TOL) может не давать те же результаты, что и -uniquetol(A,TOL).

Существует также новая функция ismembertol связана с ismember так же, как указано выше.

Ответ 4

Нет такой функции, о которой я знаю. Один сложный аспект заключается в том, что если ваш толерант, скажем, 1е-10, и у вас есть вектор со значениями, равными интервалами в 9e-11, первая и третья записи не совпадают, но первая такая же, как и второй, а второй - тот же, что и третий, - так сколько "уникальных" есть?

Одним из способов решения проблемы является то, что вы округлите свои значения до требуемой точности, а затем запустите их уникальную. Вы можете сделать это с помощью round2 (http://www.mathworks.com/matlabcentral/fileexchange/4261-round2) или используя следующий простой способ:

r = rand(100,1); % some random data
roundedData = round(r*1e6)/1e6; % round to 1e-6
uniqueValues = unique(roundedData);

Вы также можете сделать это с помощью команды hist, если точность не слишком высока:

r = rand(100,1); % create 100 random values between 0 and 1
grid = 0:0.001:1; % creates a vector of uniquely spaced values 
counts = hist(r,grid); % now you know for each element in 'grid' how many values there are
uniqueValues = grid(counts>0); % and these are the uniques

Ответ 5

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

tol = 0.001
[Y I] = sort(items(:));
uni_mask = diff([0; Y]) > tol;
%if you just want the unique items:
uni_items = Y(uni_mask); %in sorted order
uni_items = items(I(uni_mask));  % in the original order

Это не позаботится о "дрейфе"... так что-то вроде 0: 0.00001:100 действительно вернет одно уникальное значение.

Если вы хотите что-то, что может обрабатывать "дрифтинг", тогда я бы использовал histc, но вам нужно сделать какое-то приблизительное предположение о том, сколько предметов вы хотите иметь.

NUM = round(numel(items) / 10); % a rough guess
bins = linspace(min(items), max(items), NUM);
counts = histc(items, bins);
unit_items = bins(counts > 0);

BTW: Я написал это в текстовом редакторе далеко от Matlab, поэтому могут быть какие-то глупые опечатки или отключены на одну ошибку.

Надеюсь, что поможет

Ответ 6

Это трудно определить хорошо, предположим, что у вас есть допущение 1. Тогда каков будет результат [1; 2; 3; 4]?

Если у вас несколько столбцов, определение может стать еще более сложным.

Однако, если вы в основном обеспокоены проблемами округления, вы можете решить большинство из них одним из этих двух подходов:

  • Обведите все числа (учитывая ваш перенос), а затем используйте unique
  • Начните с верхней строки в качестве своего уникального набора, используйте ismemberf, чтобы определить, уникальна ли каждая новая строка, и если да, добавьте ее к вашему уникальному набору.

Первый подход имеет слабость, что 0.499999999 и 0.500000000 не могут рассматриваться как дубликаты. В то время как второй подход имеет слабость, что имеет значение для вашего ввода.

Ответ 7

Я застрял на днях с MatLab 2010, поэтому нет раундов (X, n), no _mergesimpts (по крайней мере, я не мог заставить его работать), поэтому простое решение который работает (по крайней мере, для моих данных):

Использование rat допустимого значения по умолчанию:

unique(cellstr(rat(x)))

Другой допуск:

unique(cellstr(rat(x,tol)))