Группировка относительно близких значений с помощью LINQ или цикла
Я думаю, это будет больше математики, чем C#. У меня есть массив значений с плавающей точкой, где большинство значений принадлежит одному из немногих плотно упакованных диапазонов. Вот пример (нижний предел =0, верхний предел =612):
3.4,5.0,6.1,
144.0,144.14,145.0,147.0,
273.77,275.19,279.0,
399.4,399.91,401.45,
533.26,537.0,538.9
Это один массив из 16 значений, просто разделив их, чтобы показать эти "группы". Мне нужно как-то сгруппировать их, используя Linq, ручной цикл или что-то еще, чтобы эти близкие значения попадали в одну группу.
Простая математическая операция, такая как деление на 10 (или 100), не сработает, потому что 399 попадет в другую группу, чем 401 (4-я группа в приведенном выше примере). Другим подходом было бы создать какую-то гистограмму, но я ищу кое-что простое здесь. Любая помощь будет принята с благодарностью.
2 ответа
Просто еще одна идея кластеризации с использованием GroupBy
с пользовательским компаратором
var numbers = new float[]
{
3.4f, 5.0f, 6.1f, 144.0f, 144.14f, 145.0f,
147.0f, 273.77f, 275.19f, 279.0f, 399.4f, 399.91f, 401.45f,
49, 50, 51,
533.26f, 537.0f, 538.9f
};
foreach (var group in numbers.GroupBy(i => i, new ClosenessComparer(4f)))
Console.WriteLine(string.Join(", ", group));
И обычай ClosenessComparer
:
public class ClosenessComparer : IEqualityComparer<float>
{
private readonly float delta;
public ClosenessComparer(float delta)
{
this.delta = delta;
}
public bool Equals(float x, float y)
{
return Math.Abs((x + y)/ 2f - y) < delta;
}
public int GetHashCode(float obj)
{
return 0;
}
}
И вывод:
1: 3,4 5 6,1
2: 144 144,14 145 147
3: 273,77 275,19 279
4: 399,4 399,91 401,45
6: 49 50 51
5: 533,26 537 538,9
Вот метод, который группирует элементы, если они находятся в определенной дельте (по умолчанию 4) предыдущего значения:
IEnumerable<IEnumerable<double>> GetClusters(IEnumerable<double> data,
double delta = 4.0)
{
var cluster = new List<double>();
foreach (var item in data.OrderBy(x=>x))
{
if (cluster.Count > 0 && item > cluster[cluster.Count - 1] + delta)
{
yield return cluster;
cluster = new List<double>();
}
cluster.Add(item);
}
if (cluster.Count > 0)
yield return cluster;
}
Вы можете настроить алгоритм, изменив то, что вы используете для cluster[cluster.Count - 1] + delta
, Например, вы можете использовать
cluster[0] + delta
- дельта от первого элемента в кластереcluster.Average() + delta
- дельта от среднего значения кластера до сих порcluster[cluster.Count / 2] + delta
- дельта от медианы кластера до сих пор