Возвращение уникального элемента с допуском
В Matlab есть команда unique
, которая возвращает уникальные строки в массиве. Это очень удобная команда.
Но проблема в том, что я не могу назначить терпимость к нему - в двойной точности мы всегда должны сравнивать два элемента в точности. Есть ли встроенная команда, которая возвращает уникальные элементы в пределах определенного допуска?
Ответы
Ответ 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)))