Вопрос или проблема
У меня есть много наборов данных, где измеренное значение либо «нормальное» (т.е. процесс работает), либо аномальное (т.е. процесс не работает).
К сожалению, у меня нет измерений, которые ясно указывают на то, что процесс работает, поэтому мне приходится определять это по самим данным измеренных значений. Визуально выбросы очевидны (см. ниже).
Мне удалось добиться успеха с несколькими различными техниками
- Идентификация выбросов на основе IQR
- Отсечение на основе 3 сигм
- Простое обрезание
- Модифицированный z-оценка
Все они работают иногда, но я ищу лучшее решение для своих данных.
Если вы посмотрите на изображение ниже, выбросы — это все точки, близкие к нулю. В данном конкретном случае порог 0.01 легко устраняет все плохие данные. К сожалению, порог, необходимый для данного набора данных, может значительно варьироваться.
Я был бы благодарен за любые мысли.
Вы можете попробовать построить график куантилия-куантилия: вы подгоняете свои данные к заданному распределению и строите куантиль вашей эмпирической распределения против теоретических куантилей.
Это может помочь вам выявить выбросы в вашей выборке как выбросы для подогнанного распределения.
Я новичок в Data Science, и я не знаю ни одной из упомянутых вами техник, поэтому, возможно, мой подход будет немного наивным.
Смотря на ваши данные, я бы описал их как ‘серия почти непрерывных точек с небольшими вариациями между одной точкой и следующей, кроме аномальных точек, которые лежат внизу, и разрыв между этими двумя наборами точек‘.
Если все ваши данные соответствуют этому описанию, один из способов обойти эту проблему — выявить разрыв:
def solution_1 (data, plot = False):
"""
Отсортировать все точки, найти разницу между ними (сдвинуть и вычесть), определить наибольшую разницу,
использовать среднюю точку между двумя экстремами этой разницы в качестве порога
"""
# сортировка и различие
sorted_series = data.sort_values()
diff = sorted_series - sorted_series.shift()
diff.name = "diff"
# найти экстримы
max_idx = diff.idxmax() # индекс метки
max_pos = diff.index.get_loc(max_idx) # позиция, учитывая что мы отсортировали
prev_pos = max_pos - 1
prev_idx = diff.index[prev_pos]
max_val = data[max_idx]
prev_val = data[prev_idx]
# вычислить порог
threshold = prev_val + (max_val - prev_val) / 2
if plot:
data.plot(style=".")
first = np.argmax(np.array(diff))
plt.axhline(threshold, color="green")
plt.axhline(max_val, color="red")
plt.axhline(prev_val, color="blue")
#plt.show()
return threshold
Я провел симуляцию с 1000 сгенерированных наборов данных, которые пытаются имитировать ваши, и это не сработало примерно в 13% случаев. Но затем это было, в общем, потому что фактические данные ‘касаются’ аномального набора. Вот пример неудачи:
А вот удачный пример:
Как вы видите, когда это идет не так, это идет совсем не так. Но если ваши фактические данные не касаются аномальных точек (и нормальные данные действительно имеют такое непрерывное поведение), это может сработать.
Некоторые заметки:
- Я думаю, вы, возможно, не имеете дело с выбросами. Одно из определений выбросов, которые я нашел, звучит так: “(…) выброс — это наблюдение, которое расходится с общим шаблоном в выборке“. Если вы посмотрите на ваш график, «аномальные» значения не расходятся с паттерном данных. Вместо этого они представляют собой совершенно другой паттерн. Поэтому я не уверен, что нормальные инструменты и техники обнаружения выбросов будут работать.
- Я предполагаю, что машинное обучение может дать хорошие результаты для ваших нужд. Если у вас уже есть некоторые классифицированные наборы данных, вы можете использовать их для обучения. Я никогда не работал с машинным обучением, но я вернусь к этой проблеме, как только немного научусь.
- Еще одна вещь, которую я бы сделал, — это комбинировать техники. Проведите два разных анализа и сравните их. Если они не согласуются, проверьте это вручную или адаптируйте их.
Для полноты картины, вот как я сгенерировал тестовые наборы данных:
def gen_data (points, abnormal_rate):
"""
Генерировать данные с небольшими различиями между соседними точками, кроме
'аномальных' точек, которые ближе к нулю, случайно распределены
Возвращает сгенерированную серию, новое фактическое минимальное значение (без учета аномальных точек),
aномальное_максимум, различие между мин и макс (max_diff) и серию, которая может быть
использована для идентификации аномальных точек.
Все это используется для проверки результатов и их анализа
"""
# генерирование данных без слишком больших различий между соседними точками
s = pd.Series(np.random.randn(points),range (points))
s = s.cumsum()
s.name = "gen_data"
# Переместите его вверх, чтобы вся серия была над нулем. Разница между минимальной точкой и 0 будет
# чем-то между разницей мин->макс и тысячной от нее, случайно, чтобы мы могли тестировать вариации
pMin, pMax = s.min(), s.max()
max_diff = pMax - pMin
shift = random.uniform (max_diff / 1000, max_diff)
s = s + abs(pMin) + shift
# Теперь некоторые из этих точек устанавливаем как "аномальные"
random_points = pd.Series(np.random.random(points))
random_points = random_points < abnormal_rate # random_points становится логическим и может быть использован для выбора
# максимальная аномальная точка будет где-то между 0 и новым минимальным значением
new_min = s.min()
abnormal_max = random.random() * new_min
# Мы получаем случайные числа между 0 и 1, масштабируем их до abnormal_max и устанавливаем некоторые из них
# на фактической серии (только те, которые отобраны random_poitns)
abnormal_series = pd.Series(np.random.random(points)).abs() * abnormal_max
s[random_points] = abnormal_series[random_points]
return s, new_min, abnormal_max, max_diff, random_points
Это по сути техника генерации случайных данных, описанная в документации Pandas, но с некоторыми отложенными, чтобы смоделировать аномальные данные.
Для моих тестов я запустил ее на 1000 точках и 10% аномальной ставки (gen_data(1000,0.1)
)
Я разместил этот код на gist
Ответ или решение
Как улучшить идентификацию выбросов для удаления из наборов данных
Идентификация выбросов является важной задачей в анализе данных, особенно когда речь идет о выявлении ненормальных значений, мешающих корректному анализу. Ваша ситуация, в которой необходимо определить нормальные и ненормальные состояния на основании самих измеренных значений, представляет собой сложную задачу. В этой статье мы рассмотрим несколько методов и подходов для улучшения процесса идентификации выбросов.
1. Понимание проблемы
Как вы отметили, ваши данные характеризуются почти непрерывным набором значений, с незначительными вариациями между соседними точками, за исключением ненормальных значений, которые расположены ближе к нулю. Это создает ситуацию, в которой стандартные методы обнаружения выбросов не всегда могут быть эффективными, поскольку ненормальные значения могут не только отличаться от нормальных, но и образовывать отдельную выборку.
2. Методы идентификации выбросов
Методы, которые вы уже применяли:
-
Идентификация выбросов на основе IQR: Хороша для данных, имеющих нормальное распределение, но может быть менее эффективна для ваших данных, где распределение может быть несимметричным и с разными диапазонами.
-
Тест на 3 сигмы: Этот метод работает, когда данные нормально распределены, но не может правильно обрабатывать несимметричные данные.
-
Простые пороговые значения: Установка статического порога может быть проблематичной из-за нестабильности значений по разным наборам данных.
-
Модифицированный Z-скор: Это улучшенная версия Z-скора, которая может быть полезна, но требует дополнительной настройки.
Альтернативные подходы:
2.1. Использование квантильного графика
Квантильный график (Q-Q plot) позволяет визуально сравнить ваши данные с теоретическим распределением. Это поможет увидеть, как данные отклоняются от ожидаемого и выявить выбросы, соответствующие отклонениям в распределении.
2.2. Определение промежутка между нормальными и ненормальными значениями
Ваше решение по анализу разности между отсортированными значениями представляется многообещающим. Основная идея заключается в выявлении «разрыва» между двумя группами значений.
def find_threshold(data):
sorted_series = data.sort_values()
diff = sorted_series.diff()
max_diff_index = diff.idxmax()
threshold = (sorted_series[max_diff_index - 1] + sorted_series[max_diff_index]) / 2
return threshold
2.3. Комбинирование методов
Используйте несколько методов одновременно для получения более надежных результатов. Наприме, примените комбинацию порогового значения и модифицированного Z-скора, чтобы убедиться в согласованности. Если два метода дают разные результаты, необходимо провести дополнительный анализ.
3. Использование машинного обучения
Анализ данных с использованием методов машинного обучения включает применение алгоритмов, таких как K-средние или деревья решений, для классификации ваших данных на нормальные и ненормальные значения. Это подход может предоставить более адаптивное и мощное решение, основанное на том, что данные могут иметь сложные неявные закономерности.
Заключение
Для улучшения процессов идентификации выбросов в ваших наборах данных необходимо рассмотреть многогранный подход, который будет сочетать традиционные методы, методы визуализации и алгоритмы машинного обучения. Применяя различные методы и здраво анализируя результаты, вы сможете достичь более точного определения ненормальных значений и улучшить качество анализа данных.
Следуйте этим рекомендациям для оптимизации вашего рабочего процесса, и вы сможете значительно облегчить задачу определения выбросов для удаления из ваших наборов данных.