Определение параметров сигнала методом непараметрической кластеризации DBSCAN

Подключение TSOP2136Так получилось, что я после достаточно уже приличного перерыва решил опять поиграться с TSOP-ом (т.е. приемником инфракрасного излучения) и, соответственно, с пультами дистанционного управления.

Подключение TSOP-а просто до безобразия — на одну ногу 5 Вольт питания, на другую — землю, третью подключаем к МК, желательно к ножке, которая умеет слушать прерывания.

По своему предыдущему опыту могу посоветовать только две вещи.
Во-первых, не игнорировать предлагаемые в даташите резистор и конденсатор по питанию, чтобы прием работал стабильно.
Во-вторых, внимательно смотреть назначение ног по даташиту — ходовых TSOP-ов, помимо использованного мной TSOP2136, не так уж и много, но вот ноги с одинаковыми номерами выполняют у них разные функции!

Самое же интересное началось, когда я притащил пульт и попытался восстановить структуру сигнала (пульт был от бытового электрического выключателя с ДУ ANAM — я с ним раньше уже игрался, у него пакеты короткие и кнопок мало).

На ноге OUT у TSOP-а постоянно 5 Вольт (сигнал HIGH), а в момент прихода инфракрасного пакета от пульта TSOP подтягивает ногу к нулю.
Соответственно, пакет я могу рассматривать как последовательность чисел, каждое из которых определяется продолжительностью наличия на ноге OUT TSOP-а того или иного логического уровня.

Циферок даже для короткого ANAM-овского пакета получается не так уж и мало. Глаза слегка разбигаются.
Циферки все слегка разные — потому что и пульта продолжителность интервала плывет, и ATMEGA — не самый идеальный логический анализатор, и энергосберегайка над столом в TSOP фонит.
Как именно закодирован пакет мне заранее неизвестно. Первое, что хочется сделать, хотя бы понять какие интервалы в этом сигнале встречаются. Вроде бы на глаз я вижу что низкий логический уровень всегда имеет одну и ту же продолжительность, а высокий (когда он не постоянен) — две.

Но это на глаз. А хочется уверенности да еще и поанализировать сигнал от разных кнопок. Чего глазами делать уже совсем не радует.

И тут я вспоминаю примерно похожую задачу, которую я решал с приемом информации от метеостанции Oregon Scientific — там все усугублялось гораздо более длинным пакетом в Манчестерском кодировании и передачей информациии в очень зашумленном радио-диапазоне.

И понимаю, что не могло человечество не придумать алгоритма, который позволял бы ответить на вопрос: «Вот тут у нас измерена куча палочек разной длины. Мы точно знаем что палочек разной длинны всего несколько, но сколько разных длинн точно — не знаем. А измерения каждой из плочек сделаны с определенной погрешностью — как понять, плочки какой длины использовались?»

По некоторому размышлению я понял, что мне эта задача не подсилу. И начал искать.
Интуитивно понятно, что искать надо где-то в области матстатистики.

Что удалось найти. Если что — менее, чем за пол дня. Так что могу и ошибаться.

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

Алгоритмов — много, разных типов. Принципиальное деление — алгоритмы, которые требуют, чтобы конечное число кластеров было известно (иначе же говорим, что один элемент — один кластер и задача решена, а толку?) и те, которые пытаются определить число кластеров исходя из каких-то критериев.

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

Печально потыкавшись в различные вариации k-means-а и так и не поняв, как это хотя бы попробовать, я наконец глянул в сторону алгоритмов непараметрической кластеризации (то есть тех, которые сами определяют число кластеров) и наткнулся на совершенно замечательный алгоритм DBSCAN.

Описан он вот здесь.

Описание алгоритма в псевдокоде гораздо лучше брать вот отсюда (тут наверное и сам алгоритм описан хорошо — но по-английски).

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


DBSCAN(D, eps, MinPts) {
   C = 0
   for each point P in dataset D {
      if P is visited
         continue next point
      mark P as visited
      NeighborPts = regionQuery(P, eps)
      if sizeof(NeighborPts) < MinPts
         mark P as NOISE
      else {
         C = next cluster
         expandCluster(P, NeighborPts, C, eps, MinPts)
      }
   }
}

expandCluster(P, NeighborPts, C, eps, MinPts) {
   add P to cluster C
   for each point P' in NeighborPts { 
      if P' is not visited {
         mark P' as visited
         NeighborPts' = regionQuery(P', eps)
         if sizeof(NeighborPts') >= MinPts
            NeighborPts = NeighborPts joined with NeighborPts'
      }
      if P' is not yet member of any cluster
         add P' to cluster C
   }
}

regionQuery(P, eps)
   return all points within P's eps-neighborhood (including P)


Достоинства:
1) Прост, понятен, есть готовый псевдокод
2) Подразумевает, что кластер — это такая совокупность точек, которые расположены существенно более плотно друг к другу, чем к другим точкам — а это же ровным счетом наш случай с сигналом.
3) Нечувствителенк к «шуму» и отсекает его — т.е. точки, которые сильно выбиваются из всех найденных кластеров. Шум у нас есть, и отсекать его надо уметь. Кстати, часть из алгоритмов кластеризации, включая k-means, как раз весьма чувствительны к шуму.
4) Для кластеризации необходимо указать минимальное число точек, которые признаются кластером и размер окрестности в которой мы ищем точки «близкие» друг другу. Параметры абсолютно естественные и их легко получить глядя на сигнал.

Недостатки:
1) Производительность (может быть) — даже не пытался понять, хорошая она или плохая. Не те объемы данных.
2) Кластер, соединенный тонкой «перемычкой» (в двумерном случае), будет, скорее всего, распознан как два разных кластера. Что печалька для распознователей образов, а для сигнала — скорей правильно.

Короче говоря, реализовал алгоритм на JavaScript — вроде работает. Чуть попозже попробую на реальных данных.


var d = [
3592, 1784, 628, 372, 628, 1816, 664, 312, 656, 392, 640, 1760, 632, 368, 632, 368, 604, 420, 656, 340, 632, 408, 592, 396, 668, 
3580, 1788, 660, 340, 640, 1788, 656, 336, 636, 396, 664, 1756, 628, 372, 636, 372, 628, 388, 660, 348, 652, 372, 628, 372, 664, 
3608, 1788, 636, 340, 632, 1796, 652, 348, 656, 396, 636, 1756, 628, 396, 640, 332, 640, 388, 636, 364, 660, 364, 636, 372, 652, 
3608, 1764, 660, 364, 632, 1764, 636, 360, 664, 364, 660, 1768, 628, 372, 664, 336, 652, 396, 628, 348, 628, 396, 636, 364, 636, 
3616, 1752, 636, 372, 652, 1768, 664, 336, 656, 368, 664, 336, 628, 1796, 656, 344, 632, 392, 632, 368, 656, 368, 632, 368, 668, 
3640, 1736, 664, 336, 688, 1732, 664, 336, 636, 388, 664, 336, 636, 1788, 668, 332, 632, 392, 668, 328, 672, 368, 620, 368, 668, 
3616, 1760, 632, 368, 632, 1788, 664, 336, 664, 360, 664, 336, 664, 1756, 664, 364, 596, 400, 664, 336, 660, 372, 656, 336, 664, 
3604, 1764, 632, 364, 660, 1760, 692, 308, 664, 364, 636, 364, 660, 1756, 664, 348, 652, 372, 652, 348, 664, 360, 600, 396, 640, 
3588, 1788, 656, 328, 672, 1752, 660, 348, 652, 372, 652, 348, 688, 1732, 664, 336, 628, 396, 636, 364, 636, 388, 636, 364, 664, 
3612, 1756, 636, 364, 672, 1752, 660, 340, 668, 356, 660, 340, 668, 1760, 652, 348, 628, 396, 664, 336, 664, 360, 640, 360, 664, 
3608, 1756, 664, 336, 664, 1764, 656, 340, 660, 368, 656, 1760, 640, 1768, 624, 388, 664, 336, 664, 372, 600, 396, 656, 1760, 632, 
3580, 1784, 608, 392, 664, 1756, 640, 360, 664, 360, 636, 1788, 608, 1784, 664, 364, 660, 340, 664, 344, 652, 372, 656, 1732, 632, 
3652, 1704, 636, 388, 636, 1764, 656, 368, 632, 368, 668, 1752, 660, 1764, 632, 392, 604, 400, 628, 372, 656, 368, 664, 1760, 608, 
3584, 1816, 604, 372, 628, 1796, 600, 400, 656, 392, 608, 368, 632, 1788, 664, 1752, 640, 360, 664, 376, 596, 400, 652, 1764, 632, 
3580, 1788, 604, 392, 664, 1756, 640, 372, 652, 372, 624, 376, 652, 1768, 628, 1796, 632, 368, 656, 344, 632, 392, 632, 1760, 628, 
3664, 1704, 624, 400, 664, 1752, 604, 396, 664, 344, 620, 404, 632, 1788, 664, 1756, 612, 388, 636, 360, 664, 388, 636, 1736, 656, 
3608, 1760, 636, 364, 636, 1792, 652, 348, 652, 372, 640, 360, 664, 360, 600, 1792, 632, 400, 656, 344, 656, 344, 656, 368, 656, 
3616, 1760, 656, 344, 652, 1768, 656, 344, 628, 396, 664, 336, 636, 392, 596, 1800, 632, 392, 632, 364, 632, 396, 632, 392, 644, 
3604, 1756, 672, 332, 668, 1752, 660, 348, 624, 400, 652, 348, 652, 372, 636, 1784, 600, 396, 664, 340, 636, 396, 628, 396, 628, 
3588, 1788, 656, 344, 664, 1760, 660, 340, 660, 364, 660, 340, 656, 368, 632, 1788, 588, 412, 664, 336, 664, 372, 624, 372, 656, 
3608, 1760, 640, 360, 636, 1812, 636, 336, 664, 360, 636, 372, 656, 372, 624, 1776, 620, 404, 656, 340, 632, 392, 632, 368, 656, 
3616, 1752, 636, 364, 664, 1764, 628, 372, 656, 368, 664, 336, 664, 360, 640, 1760, 632, 392, 660, 340, 660, 364, 632, 368, 632, 
3580, 1788, 664, 336, 660, 1760, 660, 340, 636, 388, 636, 364, 660, 1760, 636, 1784, 664, 348, 652, 372, 628, 372, 664, 1756, 600, 
3584, 1792, 628, 372, 656, 1772, 656, 340, 624, 400, 660, 368, 656, 1736, 640, 1784, 660, 336, 664, 336, 628, 396, 664, 1736, 656, 
3620, 1736, 636, 392, 660, 1740, 628, 396, 628, 372, 628, 424, 612, 1776, 668, 1736, 624, 400, 664, 336, 628, 396, 636, 1760, 660, 
3604, 1764, 664, 336, 636, 1784, 660, 340, 660, 364, 636, 1788, 632, 1772, 628, 388, 636, 372, 624, 400, 624, 376, 652, 1764, 664, 
3636, 1728, 640, 356, 672, 1752, 636, 360, 664, 372, 632, 1764, 652, 1772, 656, 368, 632, 368, 656, 368, 632, 368, 656, 1736, 632, 
3612, 1752, 640, 360, 664, 1764, 656, 340, 632, 392, 632, 368, 660, 1760, 640, 364, 624, 400, 632, 400, 600, 400, 600, 396, 632, 
3612, 1760, 660, 364, 608, 1788, 664, 340, 656, 364, 664, 340, 656, 1760, 636, 400, 592, 408, 652, 372, 632, 368, 632, 364, 632, 
3608, 1760, 664, 340, 660, 1768, 652, 348, 652, 372, 664, 360, 628, 1764, 640, 368, 620, 428, 632, 344, 632, 392, 632, 368, 632, 
3592, 1784, 660, 340, 632, 1788, 664, 336, 636, 388, 692, 1732, 636, 360, 664, 344, 656, 372, 624, 372, 656, 368, 628, 372, 664, 
3584, 1780, 664, 344, 656, 1764, 664, 332, 632, 392, 664, 1760, 660, 340, 660, 364, 608, 392, 636, 364, 660, 372, 628, 372, 660, 
];
var DBSCAN = function(d, eps, minPts) {
  var m = [];
  var n = 0;
  //
  var regionQuery = function(p, eps, m) {
    var points = [];
    for(var i = 0; i < m.length; i++) {
      if(Math.abs(p.val - m[i].val) < eps) points.push(m[i]);
    }
    return points;
  };
  var expandCluster = function(p, neighborPts, c, eps, minPts, m) {
    p.cluster = c;
    for(var i = 0; i < neighborPts.length; i++) {
      var p1 = neighborPts[i];
      if(!p1.visited) {
        p1.visited = true;
        var neighborPts1 = regionQuery(p1, eps, m);
        if(neighborPts1.length >= minPts) {
          neighborPts = neighborPts.concat(neighborPts1);
        }
      }
      if(p1.cluster == -1) p1.cluster = c;
    }
  };
  //
  for(var i = 0; i < d.length; i++) {
    m.push({
      idx: i,
      val: d[i],
      visited: false,
      noise: false,
      cluster: -1
    });  
  }  
  for(var i = 0; i < m.length; i++) {
    var p = m[i];
    if(p.visited) continue;
    p.visited = true;
    neighborPts = regionQuery(p, eps, m);
    if(neighborPts.length < minPts) {
      p.noise = true;
    } else {    
      var c = n++;
      expandCluster(p, neighborPts, c, eps, minPts, m);
    }
  }
  var avgs = [];
  console.log('clusters: ' + n);
  for(var i = 0; i < n; i++) {
    var msg = '';
    var cnt = 0;
    var min = -1;
    var max = -1;
    var s = 0;
    var s2 = 0;
    for(var j = 0; j < m.length; j++) if(m[j].cluster == i) {
      var val = m[j].val;      
      msg += m[j].val + ', ';
      cnt++;
      if(min == -1 || val < min) min = val;
      if(max == -1 || val > max) max = val;
      s += val;
      s2 += val * val;
    }  
    var avg = s / cnt;    
    avgs.push(avg);
    var R = max - min;
    var d = null;
    var sigma = null;
    if(cnt > 1) {
      d = (s2 - s * s / cnt) / (cnt - 1);    
      sigma = Math.sqrt(d);
    }
    var Ko = R / avg;
    var V = sigma / avg;
    console.log(
      i + ':' + 
      ' avg: ' + avg.toFixed(2) +           
      ' d: ' + d.toFixed(2) + 
      ' sigma: ' + sigma.toFixed(2) + 
      ' cnt: ' + cnt + 
      ' min: ' + min + 
      ' max: ' + max + 
      ' R: ' + R + 
      ' Ko(%): ' + (100 * Ko).toFixed(2) + 
      ' V(%): ' + (100 * V).toFixed(2)  
    );  
    console.log(msg);
  }
  console.log('---');
  console.log('noise:');
  var msg = '';
  for(var j = 0; j < m.length; j++) if(m[j].noise) msg += m[j].val + ', ';
  console.log(msg);
  console.log('---');
  console.log('Массив для секвенирования сигнала:');
  avgs = avgs.sort(function(a,b) { return Math.sign(a-b); });
  var s = '';
  for(var i = 0; i < avgs.length - 1; i++) {
    var x1 = avgs[i];
    var x2 = avgs[i+1];
    var xc = (x1 + x2) / 2;        
    s += Math.round(xc) + ', '
  }
  console.log('[ ' + s + ' ]');
  console.log('---');
}
DBSCAN(d, 100, 3);


Как использовать:

Массив d — исходные данные. Надо естественно вбить свои.

В вызове функции DBSCAN:

Второй параметр — «размер» окрестности точки, в которой мы ищем ее «соседей» — в примере стоит 100 ms, но на самом деле нужно смотреть на исходный сигнал. Если вдруг в кластер попадают очень разнородные точки — значение второго параметра надо уменьшать. Если наоборот, появляется слишком много кластеров — увеличивать.

Третий параметр — минимальное число точек для возникновения нового кластера. Чем меньше, тем меньше селективность по шуму. Как в примере — до трех компактно расположенных точек считается шумом, а не маленьким кластером. Тут главное не переусердствовать, а то какой-нибудь реальный кластер в шум выпадет.

Алгоритм вроде не сильно объемный и можно попробовать запихнуть его в ATMEG-у, но если она при этом будет заниматься еще и приемом сигнала, то с памятью определенно будет напряженка — для сигнала тоже желательно отвести буфер побольше…

UPD (17.02.2016): добавил в код реальные данные с Анамовского пульта и рассчет разных приятных статистических показателей по каждому из кластеров:
  • cnt — количество точек
  • avg — выборочное среднее
  • d — несмещенная дисперсия
  • sigma- стандартное отклонение (sqrt(d))
  • min — минимум
  • max — максимум
  • R — размах (max — min)
  • Ko(%) — коэффициент осциляции (R / avg), характеризует колеблемость крайних значений признака вокруг средней арифметической
  • V(%) — коэффициент вариации (sigma / avg), характеризует степень однородности совокупности. Совокупность считается однородной при значениях меньше 40%. При значениях больше 40% говорят о большой колеблемости признаков и совокупность считается неоднородной.
(см. Википедию и сюда)

Комментарии (9)

RSS свернуть / развернуть
Спасибо за статью. Но мне кажется, что Вы слишком усложняете задачу для вашего примера с TSOP2136.

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

Пусть есть последовательность 98, 303, 100, 299, 8

Выберем параметр дельта окрестность, пусть будет 5. Берем первое число (98), и т. к. у нас кластеров пока нет, оно и станет кластером с центром в 98. Берем второе число (303) и идем по списку кластеров. К нашему первому кластеру данное число не принадлежит т. к. выходит за рамки за рамки его дельта окрестности (93 — 103) поэтому создаем новый кластер с центром 303. Берем следующее число (100) идем по списку и определяем, что число попадает в дельта окрестность первого кластера. Относим его к первому кластеру, при этом корректируем центр первого кластера как среднее арифметическое между точками в кластере (в данном случае среднее арифметическое 98 и 100). Аналогично число 299 попадет в дельту второго кластера и его центр тоже сместится. Последнее число создаст третий кластер с центром 8.

В результате получим 3 кластера (98, 100 с центром 99), (303, 299 с центром в 301) и (8 с центром в 8). Чтобы избавиться от шумов — установим мин. размер кластера (минимальное количество точек в кластере, пусть будет 2) и тогда последний кластер отфильтруется.
0
  • avatar
  • e_mc2
  • 08 февраля 2016, 22:21
Ну, в принципе можно и такой алгоритм использовать, но у меня нет уверенности, что он корректно сработает для любой последовательности точек на входе и при более-менее сильно размазанных измерениях.

Первое что приходит в голову: последовательность
108, 96, 303, 301, 101, 95, 106, 109, 299 при дельта-окрестности 5
108 — новый кластер С1
96 — новый кластер С2
303 — новый кластер С3
101 — в С2
95 — в С2
106 — в С1
109 — в С1

То есть окрестности 100 разбились на два кластера, что не правильно. Нужно увеличивать дельту, но при этом можно потерять реальную кластеризацию, если кластера расположены близко друг от друга.

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

Второй его достоинство — в кластер собираются все его точки. А значит и центр кластера можно определить (как среднее арифметическое) с большей точностью
0
Нужно увеличивать дельту, но при этом можно потерять реальную кластеризацию, если кластера расположены близко друг от друга.
Ну, если говорить о конкретно случае пульта, то кластера близко расположены быть не могут — из соображений надежного декодирования. Так что вполне можно задать дельту в процентов 20.
0
В задачах классификаторов обычно пытаются выжать максимум из сведений о данных на входе, пытаясь применить наименее «узкий алгоритм». Может звучит непонятно, но идея в том, что если мы знаем что-то о зависимости данных на входе — нужно этим пользоваться. У нас (в данном случае) есть дополнительные сведения: передатчик нам шлет некий фиксированный интервал (Т), но, из-за ошибок передатчика, приемника и помех мы получим набор точек, которые будут сосредоточены вокруг Т (тут, часто говорят о всяких мат ожиданиях, дисперсиях и прочих распределениях Гауса). Поэтому стоит учитывать то, что наши кластера должны иметь центр (а то можно сразу перейти к нейросеттям — это тоже алгоритм классификации).

А вот DBSCAN — более общий алгоритм, он рассчитан на более общие случаи, он использует плотность точек для выделения кластера произвольной формы. Я не особо умею рисовать, но попробую изобразить случай, когда DBSCAN нам будет нам только мешать.

Здесь есть 2 кластера (с ярко выраженным центром) но они соединены «перемычкой» из «шумов» с плотностью на уровне самого кластера, из-за чего DBSCAN может объединить их в один кластер т. к. алгоритм отталкивается от расстояния между «соседями» (краями кластера, как Вы сказали).
0
Так у нас одномерный случай — насколько я понимаю, в одномерном случае перемычка невозможна по определению.

Вообще-то у нас конечно существенно более четкая задача — разделение смеси некоего заранее неизвестного количества гауссиан (или хотя бы известного), да вот только я никаких вариантов решения ее именно в такой формулировке не нашел :-(

Вполне можно было бы еще и использовать тот факт, что это гауссианы…
0
насколько я понимаю, в одномерном случае перемычка невозможна по определению. 
Почему? В одномерном случае все будет аналогично.

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

Почему? В одномерном случае все будет аналогично.

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

В многомерном случае перемычка обнаруживается за счет наличия второго измерения, по которому нет заполнения…
0
Да, Вы правы, я абсолютно не подумал, что перемычка в одномерном случае не может иметь такую же плотность как кластер.
0
Ну вот допилил все-таки «граббер TSOP-овских пакетов», собрал данные нажатий на тот же Анамовский пульт.
На входе получился вот такой массив:


var d = [
3592, 1784, 628, 372, 628, 1816, 664, 312, 656, 392, 640, 1760, 632, 368, 632, 368, 604, 420, 656, 340, 632, 408, 592, 396, 668, 
3580, 1788, 660, 340, 640, 1788, 656, 336, 636, 396, 664, 1756, 628, 372, 636, 372, 628, 388, 660, 348, 652, 372, 628, 372, 664, 
3608, 1788, 636, 340, 632, 1796, 652, 348, 656, 396, 636, 1756, 628, 396, 640, 332, 640, 388, 636, 364, 660, 364, 636, 372, 652, 
3608, 1764, 660, 364, 632, 1764, 636, 360, 664, 364, 660, 1768, 628, 372, 664, 336, 652, 396, 628, 348, 628, 396, 636, 364, 636, 
3616, 1752, 636, 372, 652, 1768, 664, 336, 656, 368, 664, 336, 628, 1796, 656, 344, 632, 392, 632, 368, 656, 368, 632, 368, 668, 
3640, 1736, 664, 336, 688, 1732, 664, 336, 636, 388, 664, 336, 636, 1788, 668, 332, 632, 392, 668, 328, 672, 368, 620, 368, 668, 
3616, 1760, 632, 368, 632, 1788, 664, 336, 664, 360, 664, 336, 664, 1756, 664, 364, 596, 400, 664, 336, 660, 372, 656, 336, 664, 
3604, 1764, 632, 364, 660, 1760, 692, 308, 664, 364, 636, 364, 660, 1756, 664, 348, 652, 372, 652, 348, 664, 360, 600, 396, 640, 
3588, 1788, 656, 328, 672, 1752, 660, 348, 652, 372, 652, 348, 688, 1732, 664, 336, 628, 396, 636, 364, 636, 388, 636, 364, 664, 
3612, 1756, 636, 364, 672, 1752, 660, 340, 668, 356, 660, 340, 668, 1760, 652, 348, 628, 396, 664, 336, 664, 360, 640, 360, 664, 
3608, 1756, 664, 336, 664, 1764, 656, 340, 660, 368, 656, 1760, 640, 1768, 624, 388, 664, 336, 664, 372, 600, 396, 656, 1760, 632, 
3580, 1784, 608, 392, 664, 1756, 640, 360, 664, 360, 636, 1788, 608, 1784, 664, 364, 660, 340, 664, 344, 652, 372, 656, 1732, 632, 
3652, 1704, 636, 388, 636, 1764, 656, 368, 632, 368, 668, 1752, 660, 1764, 632, 392, 604, 400, 628, 372, 656, 368, 664, 1760, 608, 
3584, 1816, 604, 372, 628, 1796, 600, 400, 656, 392, 608, 368, 632, 1788, 664, 1752, 640, 360, 664, 376, 596, 400, 652, 1764, 632, 
3580, 1788, 604, 392, 664, 1756, 640, 372, 652, 372, 624, 376, 652, 1768, 628, 1796, 632, 368, 656, 344, 632, 392, 632, 1760, 628, 
3664, 1704, 624, 400, 664, 1752, 604, 396, 664, 344, 620, 404, 632, 1788, 664, 1756, 612, 388, 636, 360, 664, 388, 636, 1736, 656, 
3608, 1760, 636, 364, 636, 1792, 652, 348, 652, 372, 640, 360, 664, 360, 600, 1792, 632, 400, 656, 344, 656, 344, 656, 368, 656, 
3616, 1760, 656, 344, 652, 1768, 656, 344, 628, 396, 664, 336, 636, 392, 596, 1800, 632, 392, 632, 364, 632, 396, 632, 392, 644, 
3604, 1756, 672, 332, 668, 1752, 660, 348, 624, 400, 652, 348, 652, 372, 636, 1784, 600, 396, 664, 340, 636, 396, 628, 396, 628, 
3588, 1788, 656, 344, 664, 1760, 660, 340, 660, 364, 660, 340, 656, 368, 632, 1788, 588, 412, 664, 336, 664, 372, 624, 372, 656, 
3608, 1760, 640, 360, 636, 1812, 636, 336, 664, 360, 636, 372, 656, 372, 624, 1776, 620, 404, 656, 340, 632, 392, 632, 368, 656, 
3616, 1752, 636, 364, 664, 1764, 628, 372, 656, 368, 664, 336, 664, 360, 640, 1760, 632, 392, 660, 340, 660, 364, 632, 368, 632, 
3580, 1788, 664, 336, 660, 1760, 660, 340, 636, 388, 636, 364, 660, 1760, 636, 1784, 664, 348, 652, 372, 628, 372, 664, 1756, 600, 
3584, 1792, 628, 372, 656, 1772, 656, 340, 624, 400, 660, 368, 656, 1736, 640, 1784, 660, 336, 664, 336, 628, 396, 664, 1736, 656, 
3620, 1736, 636, 392, 660, 1740, 628, 396, 628, 372, 628, 424, 612, 1776, 668, 1736, 624, 400, 664, 336, 628, 396, 636, 1760, 660, 
3604, 1764, 664, 336, 636, 1784, 660, 340, 660, 364, 636, 1788, 632, 1772, 628, 388, 636, 372, 624, 400, 624, 376, 652, 1764, 664, 
3636, 1728, 640, 356, 672, 1752, 636, 360, 664, 372, 632, 1764, 652, 1772, 656, 368, 632, 368, 656, 368, 632, 368, 656, 1736, 632, 
3612, 1752, 640, 360, 664, 1764, 656, 340, 632, 392, 632, 368, 660, 1760, 640, 364, 624, 400, 632, 400, 600, 400, 600, 396, 632, 
3612, 1760, 660, 364, 608, 1788, 664, 340, 656, 364, 664, 340, 656, 1760, 636, 400, 592, 408, 652, 372, 632, 368, 632, 364, 632, 
3608, 1760, 664, 340, 660, 1768, 652, 348, 652, 372, 664, 360, 628, 1764, 640, 368, 620, 428, 632, 344, 632, 392, 632, 368, 632, 
3592, 1784, 660, 340, 632, 1788, 664, 336, 636, 388, 692, 1732, 636, 360, 664, 344, 656, 372, 624, 372, 656, 368, 628, 372, 664, 
3584, 1780, 664, 344, 656, 1764, 664, 332, 632, 392, 664, 1760, 660, 340, 660, 364, 608, 392, 636, 364, 660, 372, 628, 372, 660, 
];


Напустил на него DBSCAN. Результат:


clusters: 4

0: 3592, 3580, 3608, 3608, 3616, 3640, 3616, 3604, 3588, 3612, 3608, 3580, 3652, 3584, 3580, 3664, 3608, 3616, 3604, 3588, 3608, 3616, 3580, 3584, 3620, 3604, 3636, 3612, 3612, 3608, 3592, 3584,  avg: 3606

1: 1784, 1816, 1760, 1788, 1788, 1756, 1788, 1796, 1756, 1764, 1764, 1768, 1752, 1768, 1796, 1736, 1732, 1788, 1760, 1788, 1756, 1764, 1760, 1756, 1788, 1752, 1732, 1756, 1752, 1760, 1756, 1764, 1760, 1768, 1760, 1784, 1756, 1788, 1784, 1732, 1704, 1764, 1752, 1764, 1760, 1816, 1796, 1788, 1752, 1764, 1788, 1756, 1768, 1796, 1760, 1704, 1752, 1788, 1756, 1736, 1760, 1792, 1792, 1760, 1768, 1800, 1756, 1752, 1784, 1788, 1760, 1788, 1760, 1812, 1776, 1752, 1764, 1760, 1788, 1760, 1760, 1784, 1756, 1792, 1772, 1736, 1784, 1736, 1736, 1740, 1776, 1736, 1760, 1764, 1784, 1788, 1772, 1764, 1728, 1752, 1764, 1772, 1736, 1752, 1764, 1760, 1760, 1788, 1760, 1760, 1768, 1764, 1784, 1788, 1732, 1780, 1764, 1760,  avg: 1766

2: 628, 628, 664, 656, 640, 632, 632, 604, 656, 632, 592, 668, 660, 640, 656, 636, 664, 628, 636, 628, 660, 652, 628, 664, 636, 632, 652, 656, 636, 628, 640, 640, 636, 660, 636, 652, 660, 632, 636, 664, 660, 628, 664, 652, 628, 628, 636, 636, 636, 652, 664, 656, 664, 628, 656, 632, 632, 656, 632, 668, 664, 688, 664, 636, 664, 636, 668, 632, 668, 672, 620, 668, 632, 632, 664, 664, 664, 664, 664, 596, 664, 660, 656, 664, 632, 660, 692, 664, 636, 660, 664, 652, 652, 664, 600, 640, 656, 672, 660, 652, 652, 688, 664, 628, 636, 636, 636, 664, 636, 672, 660, 668, 660, 668, 652, 628, 664, 664, 640, 664, 664, 664, 656, 660, 656, 640, 624, 664, 664, 600, 656, 632, 608, 664, 640, 664, 636, 608, 664, 660, 664, 652, 656, 632, 636, 636, 656, 632, 668, 660, 632, 604, 628, 656, 664, 608, 604, 628, 600, 656, 608, 632, 664, 640, 664, 596, 652, 632, 604, 664, 640, 652, 624, 652, 628, 632, 656, 632, 632, 628, 624, 664, 604, 664, 620, 632, 664, 612, 636, 664, 636, 656, 636, 636, 652, 652, 640, 664, 600, 632, 656, 656, 656, 656, 656, 652, 656, 628, 664, 636, 596, 632, 632, 632, 632, 644, 672, 668, 660, 624, 652, 652, 636, 600, 664, 636, 628, 628, 656, 664, 660, 660, 660, 656, 632, 588, 664, 664, 624, 656, 640, 636, 636, 664, 636, 656, 624, 620, 656, 632, 632, 656, 636, 664, 628, 656, 664, 664, 640, 632, 660, 660, 632, 632, 664, 660, 660, 636, 636, 660, 636, 664, 652, 628, 664, 600, 628, 656, 656, 624, 660, 656, 640, 660, 664, 628, 664, 656, 636, 660, 628, 628, 628, 612, 668, 624, 664, 628, 636, 660, 664, 636, 660, 660, 636, 632, 628, 636, 624, 624, 652, 664, 640, 672, 636, 664, 632, 652, 656, 632, 656, 632, 656, 632, 640, 664, 656, 632, 632, 660, 640, 624, 632, 600, 600, 632, 660, 608, 664, 656, 664, 656, 636, 592, 652, 632, 632, 632, 664, 660, 652, 652, 664, 628, 640, 620, 632, 632, 632, 632, 660, 632, 664, 636, 692, 636, 664, 656, 624, 656, 628, 664, 664, 656, 664, 632, 664, 660, 660, 608, 636, 660, 628, 660,  avg: 645

3: 372, 312, 392, 368, 368, 420, 340, 408, 396, 340, 336, 396, 372, 372, 388, 348, 372, 372, 340, 348, 396, 396, 332, 388, 364, 364, 372, 364, 360, 364, 372, 336, 396, 348, 396, 364, 372, 336, 368, 336, 344, 392, 368, 368, 368, 336, 336, 388, 336, 332, 392, 328, 368, 368, 368, 336, 360, 336, 364, 400, 336, 372, 336, 364, 308, 364, 364, 348, 372, 348, 360, 396, 328, 348, 372, 348, 336, 396, 364, 388, 364, 364, 340, 356, 340, 348, 396, 336, 360, 360, 336, 340, 368, 388, 336, 372, 396, 392, 360, 360, 364, 340, 344, 372, 388, 368, 368, 392, 400, 372, 368, 372, 400, 392, 368, 360, 376, 400, 392, 372, 372, 376, 368, 344, 392, 400, 396, 344, 404, 388, 360, 388, 364, 348, 372, 360, 360, 400, 344, 344, 368, 344, 344, 396, 336, 392, 392, 364, 396, 392, 332, 348, 400, 348, 372, 396, 340, 396, 396, 344, 340, 364, 340, 368, 412, 336, 372, 372, 360, 336, 360, 372, 372, 404, 340, 392, 368, 364, 372, 368, 336, 360, 392, 340, 364, 368, 336, 340, 388, 364, 348, 372, 372, 372, 340, 400, 368, 336, 336, 396, 392, 396, 372, 424, 400, 336, 396, 336, 340, 364, 388, 372, 400, 376, 356, 360, 372, 368, 368, 368, 368, 360, 340, 392, 368, 364, 400, 400, 400, 396, 364, 340, 364, 340, 400, 408, 372, 368, 364, 340, 348, 372, 360, 368, 428, 344, 392, 368, 340, 336, 388, 360, 344, 372, 372, 368, 372, 344, 332, 392, 340, 364, 392, 364, 372, 372,  avg: 367

---
noise:
(пустая строка)
---


Для практических целей самое интересное — среднее значение по кластеру. Именно по нему можно отстраивать перекодировку сигнала в более лаконичный и сравнибельный вид.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.