Джулия — AssertionError в алгоритме K-medoids

Я использую K-medoids, чтобы сократить свои данные (потенциальные будущие сценарии цены акций) с 400 сценариев до n = 25. Я вычислил амтрикс расстояния на основе евклидова расстояния:

C = pairwise(Euclidean(), data', data')

где data мои 400 сценариев - файл данных можно найти по ссылке . Затем я запускаю К-медоиды:

kmedoids(C, n, init = :kmpp)

Я получаю ошибку:

ERROR: AssertionError: !(isempty(grp))

Кто-нибудь из вас знает, что может быть не так? Кажется, на Python он работает нормально (из sklearn_extra.cluster import KMedoids), но я думаю, что две функции K-medoids разные. Мне не удалось найти документацию о том, почему это не удается.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
61
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

(Пример)

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

import Clustering.kmedoids
import Distances.Euclidean
import Distances.pairwise
import DelimitedFiles.readdlm

data = readdlm("data.csv")

(Теперь ваш код воспроизводит ошибку.)

n = 25
C = pairwise(Euclidean(), data', data')
kmedoids(C, n, init = :kmpp)
ERROR: AssertionError: !(isempty(grp))
Stacktrace:
 [1] _find_medoid(dist::Matrix{Float64}, grp::Vector{Int64})
   @ Clustering C:\Users\...\.julia\packages\Clustering\JwhfU\src\kmedoids.jl:229
 [2] _kmedoids!(medoids::Vector{Int64}, dist::Matrix{Float64}, maxiter::Int64, tol::Float64, displevel::Int64)
   @ Clustering C:\Users\...\.julia\packages\Clustering\JwhfU\src\kmedoids.jl:148
 [3] kmedoids(dist::Matrix{Float64}, k::Int64; init::Symbol, maxiter::Int64, tol::Float64, display::Symbol)
   @ Clustering C:\Users\...\.julia\packages\Clustering\JwhfU\src\kmedoids.jl:77
 [4] top-level scope
   @ REPL[13]:1

Отвечать

Возможно, в данных недостаточно различных значений для создания 25 или более различных непустых кластеров.

Set(data)

производит

Set{Float64} with 24 elements:
  -26.917
  12.619999999999997
  12.811
  12.79
  -4.722000000000001
  -27.619000000000003
  12.771
  11.009999999999998
  12.800999999999995
  -27.649
  9.980999999999995
  12.780999999999999
  10.780000000000001
  12.820999999999998
  12.790999999999997
  -27.001
  -27.586000000000002
  10.210999999999999
  7.9809999999999945
  11.989999999999995
  -4.579000000000001
  7.9709999999999965
  9.190999999999995
  -11.868000000000002

Справочная документация ?kmediods REPL заканчивается примечанием об алгоритме.

Функция реализует алгоритм стиля K-средних вместо PAM (разделение вокруг медоидов). K-означает стиль Алгоритм сходится за меньшее количество итераций, но было показано, что он дает худшие результаты (общие затраты выше на 10-20%) (см. например Шуберт и Руссеу (2019)).

Clustering.kmedoids документация ссылки

Тейтц, М.Б. и Барт П. (1968). Эвристические методы оценки обобщенной вершинной медианы взвешенного графа. Исследование операций, 16(5), 955–961. doi:10.1287/opre.16.5.955

Шуберт Э. и Русси П.Дж. (2019). Ускоренная кластеризация k-медоидов: улучшение алгоритмов PAM, CLARA и CLARANS. СИСАП, 171–187. doi:10.1007/978-3-030-32047-8_16

Думаю, вы абсолютно правы — большое спасибо :) И спасибо за отзыв по вопросу в целом :)

Lyft 30.06.2024 19:58

Пожалуйста!

user9712582 30.06.2024 20:18

Основная причина находится в ответе @user9712582.

В определенных ситуациях вы предпочитаете иметь пустые классы, а не ошибку. В таких случаях вы можете использовать BetaML.KMedoidsClusterer:


julia> using HTTP, DelimitedFiles, Pipe, BetaML, Statistics

julia> url = "https://github.com/user-attachments/files/16040190/data.csv";

julia> data = @pipe HTTP.get(url).body  |> readdlm(_);

julia> unique(data)
24-element Vector{Float64}:
  12.79
  12.790999999999997
  12.619999999999997
  12.820999999999998
 -11.868000000000002
  12.771
  12.800999999999995
 -27.619000000000003
   9.190999999999995
   9.980999999999995
 -27.649
   ⋮
 -26.917
  12.811
  -4.579000000000001
  10.780000000000001
  10.210999999999999
 -27.001
  11.989999999999995
   7.9809999999999945
  -4.722000000000001
 -27.586000000000002

julia> m = KMedoidsClusterer(n_classes=25, initialisation_strategy = "grid")
KMedoidsClusterer - A K-Medoids Model (unfitted)

julia> classes = fit!(m,data)
500-element Vector{Int64}:
 25
 25
 25
 25
 25
 25
 10
 25
 10
 10
 25
  ⋮
 10
 10
 10
 25
 25
 25
  1
 10
 10
 10

julia> unique(classes)
6-element Vector{Int64}:
 25
 10
  1
 23
 24
 15

Тем не менее, для ваших данных достаточно 4 классов:

pd = pairwise(data)
for i in 1:25
    m = KMedoidsClusterer(n_classes=i,initialisation_strategy = "grid")
    classes = fit!(m,data)
    s = mean(silhouette(pd,classes))
    n_unique = length(unique(classes))
    println("Avg siluette for $i classes ($n_unique unique): $s")
end


Avg siluette for 1 classes (1 unique): 1.0
Avg siluette for 2 classes (2 unique): 0.9767906918585568
Avg siluette for 3 classes (3 unique): 0.99164186448072
Avg siluette for 4 classes (4 unique): 0.9929543302371584
Avg siluette for 5 classes (4 unique): 0.9929543302371584
Avg siluette for 6 classes (4 unique): 0.9929543302371584
Avg siluette for 7 classes (5 unique): 0.9858369866514152
Avg siluette for 8 classes (5 unique): 0.9858369866514152
Avg siluette for 9 classes (5 unique): 0.9858369866514152
Avg siluette for 10 classes (5 unique): 0.9858369866514152
Avg siluette for 11 classes (5 unique): 0.9858369866514152
Avg siluette for 12 classes (5 unique): 0.9858369866514152
Avg siluette for 13 classes (5 unique): 0.9858369866514152
Avg siluette for 14 classes (5 unique): 0.9858369866514152
Avg siluette for 15 classes (5 unique): 0.9858369866514152
Avg siluette for 16 classes (5 unique): 0.9858369866514152
Avg siluette for 17 classes (6 unique): 0.9872595910397074
Avg siluette for 18 classes (6 unique): 0.9872595910397074
Avg siluette for 19 classes (6 unique): 0.9872595910397074
Avg siluette for 20 classes (6 unique): 0.9872595910397074
Avg siluette for 21 classes (6 unique): 0.9872595910397074
Avg siluette for 22 classes (6 unique): 0.9872595910397074
Avg siluette for 23 classes (6 unique): 0.9861063345422215
Avg siluette for 24 classes (6 unique): 0.9861063345422215
Avg siluette for 25 classes (6 unique): 0.9861063345422215


Спасибо большое, это именно то, что я хотел :)

Lyft 02.07.2024 07:33

Другие вопросы по теме