Возвращает середину n (значения не индекс) из коллекции - PullRequest
7 голосов
/ 18 апреля 2011

У меня есть List<int>, и мне нужно удалить выбросы, поэтому я хочу использовать подход, в котором я беру только среднее n Я хочу средние значения, а не индекс.

Например, учитывая следующий список, если бы я хотел получить средние 80%, я бы ожидал, что 11 и 100 будут удалены.

11,22,22,33,44,44,55,55,55,100.

Есть ли простой / встроенный способ сделать это в LINQ?

Ответы [ 6 ]

11 голосов
/ 18 апреля 2011

У меня есть List<int>, и мне нужно удалить выбросы, поэтому я хочу использовать подход, в котором я беру только середину n. Я хочу средний с точки зрения ценностей, а не индекса.

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

Предполагая, что это нормальное (гауссово) распределение, вот что вы хотите сделать.

Сначала вычислите среднее . Это легко; это просто сумма, деленная на количество предметов.

Во-вторых, вычислите стандартное отклонение . Стандартное отклонение является мерой того, насколько «разбросаны» данные вокруг среднего значения. Вычислить это по:

  • принять разницу каждой точки от среднего
  • квадрат разница
  • взять среднее значение квадратов - это дисперсия
  • взять квадратный корень из дисперсии - это стандартное отклонение

При нормальном распределении 80% пунктов находятся в пределах 1,2 стандартных отклонений от среднего. Например, предположим, что среднее значение равно 50, а стандартное отклонение равно 20. Можно ожидать, что 80% выборки упадет между 50 - 1,2 * 20 и 50 + 1,2 * 20. Затем вы можете отфильтровать элементы из списка. которые находятся за пределами этого диапазона.

Обратите внимание, что это , а не удаление "выбросов". Это удаляет элементы, которые имеют более 1,2 стандартных отклонения от среднего значения, чтобы получить 80% интервал вокруг среднего значения. В нормальном распределении ожидают увидеть «выбросы» на регулярной основе. 99,73% элементов находятся в пределах трех стандартных отклонений от среднего значения, что означает, что если у вас есть тысяча наблюдений, совершенно нормально видеть два или три наблюдения, превышающие три стандартных отклонения за пределами среднего значения! Фактически, где-нибудь, скажем, до пяти наблюдений, превышающих три стандартных отклонения от среднего значения, если дано тысяча наблюдений, вероятно, не указывает на выброс .

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

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

4 голосов
/ 18 апреля 2011

Вы можете использовать метод Enumerable.OrderBy для сортировки списка, затем использовать функции Enumerable.Skip и Enumerable.Take, например:

var result = nums.OrderBy(x => x).Skip(1).Take(8);

Где nums - ваш список целых чисел.

Выяснение, какие значения использовать в качестве аргументов для Skip и Take должны выглядеть примерно так, если вы просто хотите "средние n значения":

nums.OrderBy(x => x).Skip((nums.Count - n) / 2).Take(n);

Однако когда результат (nums.Count - n) / 2 не является целым числом, как вы хотите, чтобы код вел себя?

2 голосов
/ 18 апреля 2011

Обычно, если вы хотите исключить статистические выбросы из набора значений, вы вычисляете среднее арифметическое и стандартное отклонение для набора, а затем удаляете значения, лежащие дальше от среднего значения, чем вы хотели (измерить в стандартном отклонения). Нормальное распределение & mdash; ваш классический колоколообразный изгиб & ndash; обладает следующими свойствами:

  • Около 68% данных будут находиться в пределах +/- 1 стандартного отклонения от среднего.
  • Около 95% данных будут находиться в пределах +/- 2 стандартных отклонения от среднего.
  • Около 99,7% данных будут находиться в пределах +/- 3 стандартных отклонения от среднего.

Вы можете получить методы расширения Linq для вычисления стандартного отклонения (и других статистических функций) в http://www.codeproject.com/KB/linq/LinqStatistics.aspx

2 голосов
/ 18 апреля 2011

При условии, что вы не занимаетесь средневзвешенным смешным делом:

List<int> ints = new List<int>() { 11,22,22,33,44,44,55,55,55,100 };

int min = ints.Min();
double range = (ints.Max() - min);

var results = ints.Select(o => new { IntegralValue = o, Weight = (o - ints.Min()) / range} );

results.Where(o => o.Weight >= .1 && o.Weight < .9);

Затем вы можете отфильтровать вес по мере необходимости. Опустите верх / нижнюю часть n % по желанию.

В вашем случае:

results.Where(o => o.Weight >= .1 && o.Weight < .9)

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

public static class Lulz
{
    public static List<int> MiddlePercentage(this List<int> ints, double Percentage)
    {
        int min = ints.Min();
        double range = (ints.Max() - min);

        var results = ints.Select(o => new { IntegralValue = o, Weight = (o - ints.Min()) / range} );

        double tolerance = (1 - Percentage) / 2;
        return results.Where(o => o.Weight >= tolerance && o.Weight < 1 - tolerance).Select(o => o.IntegralValue).ToList();
    }
}

Использование:

List<int> ints = new List<int>() { 11,22,22,33,44,44,55,55,55,100 };
var results = ints.MiddlePercentage(.8);
0 голосов
/ 06 августа 2018

У меня есть Список, и мне нужно удалить выбросы, поэтому я хочу использовать подход, в котором я беру только середину n. Я хочу средние значения, а не индекс.

Если я правильно понимаю, мы хотим сохранить любые значения, которые попадают в средние 80% диапазона 11-100, или

min + (max - min - (max - min) * 0.8) / 2 < x < max - (max - min - (max - min) * 0.8) / 2

Предполагая упорядоченный список, мы можем пропустить, пока значения ниже, чем lowerBound, а затем взять, пока числа меньше, чем upperBound

public void Calculalte()
{
    var numbers = new[] { 11, 22, 22, 33, 44, 44, 55, 55, 55, 100 };

    var percentage = 0.8;

    var result = RemoveOutliers(numbers, percentage);
}

private IEnumerable<int> RemoveOutliers(int[] numbers, double percentage)
{
    int min = numbers.First();
    int max = numbers.Last();
    double range = (max - min);
    double lowerBound = min + (range - range * percentage) / 2;
    double upperBound = max - (range - range * percentage) / 2;
    return numbers.SkipWhile(n => n < lowerBound).TakeWhile(n => n < upperBound);   
}
0 голосов
/ 03 февраля 2016

Я не собираюсь подвергать сомнению правильность вычисления выбросов, поскольку у меня была аналогичная потребность сделать именно этот вид отбора. Ответ на конкретный вопрос о взятии среднего n:

List<int> ints = new List<int>() { 11,22,22,33,44,44,55,55,55,100 };
var result = ints.Skip(1).Take(ints.Count() - 2);

Это пропускает первый элемент и останавливается перед последним, давая вам только средние n элементов. Вот ссылка на .NET Fiddle, демонстрирующую этот запрос.

https://dotnetfiddle.net/p1z7em

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...