Принятие у меня есть список сдвигов для события (в дате начала формата / время, дата окончания / время) - является там своего рода алгоритмом, который я мог использовать для создания обобщенной сводки расписания? Большинству сдвигов довольно свойственно попасть в своего рода общий шаблон повторений (т.е. понедельники с 9:00 до 13:00, вторники с 10:00 до 15:00, и т.д.). Однако там может (и быть) исключения к этому правилу (например, один из сдвигов упал на праздник и был перенесен в течение следующего дня). Было бы хорошо исключать тех, которые из моей "сводки", в то время как я надеюсь предоставлять более общий ответ того, когда делает это событие, обычно происходят.
Я предполагаю, что ищу своего рода статистический метод определить происшествия дня и времени и создать описание на основе самых частых происшествий, найденных в списке. Есть ли своего рода общий алгоритм для чего-то вроде этого? Кто-либо создал что-то подобное?
Идеально я ищу решение в C# или VB.NET, но не возражаю портировать с любого другого языка.
Заранее спасибо!
Вы можете использовать Кластерный анализ .
Кластеризация - это способ разделения набора данных на похожие компоненты (подмножества). Понятие «подобие» подразумевает некоторое определение «расстояния» между точками. Существует множество обычных формул для расстояния, в том числе обычное евклидово расстояние.
Прежде чем указывать вам на причуды торговли, давайте покажем практический случай вашей проблемы, чтобы вы могли принять участие в алгоритмах и пакетах или отказаться от них заранее.
Для простоты я смоделировал проблему в системе Mathematica, поскольку кластерный анализ включен в программное обеспечение и очень прост в настройке.
Сначала сгенерируйте данные. Формат: {ДЕНЬ, ВРЕМЯ НАЧАЛА, ВРЕМЯ КОНЕЦ}.
К временам начала и окончания добавлена случайная величина (+ полчаса, ноль, -половина часа}, чтобы показать способность алгоритма справляться с «шумом».
Есть три дня, три смены в день и одна дополнительная. (последний) «аномальный» сдвиг, который начинается в 7 утра и заканчивается в 9 утра (бедняги!).
В каждой «нормальной» смене 150 событий, а в исключительной - только два.
Как видите, некоторые сдвиги не очень далеки друг от друга.
Я включаю код в Mathematica, на случай, если у вас есть доступ к программному обеспечению. Я стараюсь избегать использования функционального синтаксиса, чтобы сделать код «иностранцам» легче читать.
Вот код генерации данных:
Rn[] := 0.5 * RandomInteger[{-1, 1}];
monshft1 = Table[{ 1 , 10 + Rn[] , 15 + Rn[] }, {150}]; // 1
monshft2 = Table[{ 1 , 12 + Rn[] , 17 + Rn[] }, {150}]; // 2
wedshft1 = Table[{ 3 , 10 + Rn[] , 15 + Rn[] }, {150}]; // 3
wedshft2 = Table[{ 3 , 14 + Rn[] , 17 + Rn[] }, {150}]; // 4
frishft1 = Table[{ 5 , 10 + Rn[] , 15 + Rn[] }, {150}]; // 5
frishft2 = Table[{ 5 , 11 + Rn[] , 15 + Rn[] }, {150}]; // 6
monexcp = Table[{ 1 , 7 + Rn[] , 9 + Rn[] }, {2}]; // 7
Теперь мы объединяем данные, получая один большой набор данных:
data = Join[monshft1, monshft2, wedshft1, wedshft2, frishft1, frishft2, monexcp];
Давайте запустим кластерный анализ данных:
clusters = FindClusters[data, 7, Method->{"Agglomerate","Linkage"->"Complete"}]
«Агломерат» и «Связь» -> «Завершить» "- это два варианта тонкой настройки методов кластеризации, реализованных в системе Mathematica. Они просто указывают, что мы пытаемся найти очень компактные кластеры.
Я указал попытаться обнаружить 7 кластеров. Если нужное количество смен неизвестно, вы можете попробовать несколько разумных значений и посмотреть результаты или позволить алгоритму выбрать более подходящее значение.
Мы можем получить диаграмму с результатами, каждый кластер выделен другим цветом (не обращайте внимания на код)
ListPointPlot3D[ clusters,
PlotStyle->{{PointSize[Large], Pink}, {PointSize[Large], Green},
{PointSize[Large], Yellow}, {PointSize[Large], Red},
{PointSize[Large], Black}, {PointSize[Large], Blue},
{PointSize[Large], Purple}, {PointSize[Large], Brown}},
AxesLabel -> {"DAY", "START TIME", "END TIME"}]
И результат:
Где вы можете четко видеть наши семь кластеров.
Это решает часть вашей проблемы: идентификация данных. Теперь вы также хотите иметь возможность маркировать его.
Итак, мы возьмем каждый кластер и возьмем средние (округленные):
Table[Round[Mean[clusters[[i]]]], {i, 7}]
Результат:
Day Start End
{"1", "10", "15"},
{"1", "12", "17"},
{"3", "10", "15"},
{"3", "14", "17"},
{"5", "10", "15"},
{"5", "11", "15"},
{"1", "7", "9"}
И с этим вы снова получите свои семь классов.
Теперь, возможно, вы хотите классифицировать смены, независимо от дня. Если одни и те же люди выполняют одну и ту же задачу в одно и то же время каждый день, поэтому нет смысла называть это «смена понедельника с 10 до 15», потому что это происходит также по средам и пятницам (как в нашем примере).
Давайте проанализируем данные, не обращая внимания на первый столбец:
clusters=
FindClusters[Take[data, All, -2],Method->{"Agglomerate","Linkage"->"Complete"}];
В этом случае мы не выбираем количество кластеров для извлечения, оставляя решение на усмотрение пакета.
Результат:
Вы можете видеть, что было идентифицировано пять кластеров.
Давайте попробуем «пометить» их, как раньше:
Grid[Table[Round[Mean[clusters[[i]]]], {i, 5}]]
Результат:
START END
{"10", "15"},
{"12", "17"},
{"14", "17"},
{"11", "15"},
{ "7", "9"}
Это именно то, что мы «подозревали»: каждый день в одно и то же время происходят повторяющиеся события, которые можно сгруппировать вместе.
Если у вас есть (или планируете иметь) смены, которые начинаются в один день и заканчиваются в следующий, лучше смоделировать
{Start-Day Start-Hour Length} // Correct!
, чем
{Start-Day Start-Hour End-Day End-Hour} // Incorrect!
. Это потому, что, как и с любым статистическим методом корреляция между переменными должна быть явной, иначе метод не сработает. Этот принцип может выглядеть примерно так: «Держите данные вашего кандидата нормализованными». Обе концепции почти одинаковы (атрибуты должны быть независимыми).
--- Редактировать конец ---
К настоящему времени, я думаю, вы хорошо понимаете, какие вещи можно делать с помощью такого рода анализа.
HTH!
Я не думаю, что существует готовый алгоритм, поэтому, к сожалению, вам нужно что-то придумать самостоятельно. Поскольку проблема не совсем четко определена (с математической точки зрения), она потребует тестирования на некоторых «реальных» данных, которые были бы достаточно репрезентативными, и небольшой корректировки.
Я бы начал с разделения ваших смен на будние дни (потому что, если я правильно понимаю, вы хотите просматривать еженедельные данные) - так что для каждого буднего дня у нас есть смены, которые случаются в этот день. Затем для каждого дня я сгруппировал бы сдвиги, которые происходят в одно и то же время (или «примерно» в одно и то же время - здесь вам нужно придумать некоторую эвристику, то есть время начала и окончания не отклоняется от среднего в группе на более 15 минут или 30 минут). Теперь нам нужна еще одна эвристика, чтобы решить, актуальна ли эта группа, например, если смена с 13:00 до 15:00 в понедельник произошла только один раз, это, вероятно, не имеет значения, но если это произошло как минимум в 70% понедельников, охватываемых данными, то это соответствующие. И теперь ваши соответствующие группы на каждый день недели сформируют расписание, которое вам нужно.
Можем ли мы увидеть пример набора данных? Если это действительно "чистые" данные, то вы можете просто найти режим начального и конечного времени.
Одним из вариантов может быть обозначение всех начальных времен как +1, а конечных -1, затем создайте таблицу из трех столбцов: время (начальное и конечное), обозначение (+1 или -1), количество сотрудников в это время (начинается с нуля и добавляет или вычитает сотрудников, используя обозначение) и отсортируйте все это в порядке возрастания.
Теперь этот временной ряд представляет собой сводный описатель уровня персонала, а метки также являются серией. Теперь вы можете применить статистику временного ряда к обоим показателям для поиска ежедневных, еженедельных или ежемесячных закономерностей.