Ответ 1
Как насчет этого?
points[[
Flatten[Position[clusterIndices, #]]
]] & /@
Union[clusterIndices]
В Mathematica у меня есть список точечных координат
size = 50;
points = Table[{RandomInteger[{0, size}], RandomInteger[{0, size}]}, {i, 1, n}];
и список индексов кластера, эти точки принадлежат
clusterIndices = {1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1};
какой самый простой способ разделить точки на два отдельных списка на основе значений clusterIndices?
EDIT: Решение, с которым я столкнулся:
pointIndices =
Map[#[[2]] &,
GatherBy[MapIndexed[{#1, #2[[1]]} &, clusterIndices], First],
{2}];
pointsByCluster = Map[Part[points, #] &, pointIndices];
Это лучший способ сделать это?
Как насчет этого?
points[[
Flatten[Position[clusterIndices, #]]
]] & /@
Union[clusterIndices]
Как отметили @High Performance Mark и @Nicholas Wilson, я начал бы комбинировать два списка вместе через Transpose
или Thread
. В этом случае
In[1]:= Transpose[{clusterIndices, points}]==Thread[{clusterIndices, points}]
Out[1]:= True
В какой-то момент я посмотрел, что было быстрее, и я думаю, что Thread
немного быстрее. Но это действительно имеет значение, когда вы используете очень длинные списки.
@High Performance Mark дает хорошее представление о Select
. Но это позволит вам вытащить только один кластер за раз. Код для выбора кластера 1 выглядит следующим образом:
Select[Transpose[{clusterIndices, points}], #[[1]]==1& ][[All, All, 2]]
Поскольку вы, кажется, хотите сгенерировать все кластеры, я бы предложил сделать следующее:
GatherBy[Transpose[{clusterIndices, points}], #[[1]]& ][[All, All, 2]]
который имеет то преимущество, что он является одним лайнером, и единственная сложная часть заключалась в выборе правильного Part
результирующего списка. Трюк в определении количества членов All
необходим, чтобы отметить, что
Transpose[{clusterIndices, points}][[All,2]]
требуется, чтобы вернуть точки из перенесенного списка. Но "кластеризованный" список имеет один дополнительный уровень, следовательно второй All
.
Следует отметить, что второй параметр в GatherBy
- это функция, которая принимает один параметр и может быть заменена любой функцией, которую вы хотите использовать. Таким образом, это очень полезно. Однако, если вы хотите преобразовать свои данные в качестве своего сбора, я бы посмотрел на Reap
и Sow
.
Изменить: Reap
и Sow
несколько используются и достаточно мощные. Они несколько запутывают использование, но я подозреваю, что GatherBy
реализован с использованием их внутри. Например,
Reap[ Sow[#[[2]], #[[1]] ]& /@ Transpose[{clusterIndices, points}], _, #2& ]
делает то же самое, что и мой предыдущий код, без хлопот по снятию индексов из точек. По существу, Sow
тегирует каждую точку своим индексом, тогда Reap собирает все теги (_
для второго параметра) и выводит только точки. Лично я использую это вместо GatherBy, и я закодировал его в функцию, которую я загружаю, следующим образом:
SelectEquivalents[x_List,f_:Identity, g_:Identity, h_:(#2&)]:=
Reap[Sow[g[#],{f[#]}]&/@x, _, h][[2]];
Примечание: этот код является измененной формой того, что было в файлах справки в 5.x. Но файлы справки 6.0 и 7.0 удалили много полезных примеров, и это было одним из них.
Вот краткий способ сделать это, используя новую функцию SplitBy
в версии 7.0, которая должна быть довольно быстрой:
SplitBy[Transpose[{points, clusterIndices}], Last][[All, All, 1]]
Если вы не используете 7.0, вы можете реализовать это как:
Split[Transpose[{points, clusterIndices}], Last[#]==Last[#2]& ][[All, All, 1]]
Извините, я не видел, что вам нужны только две группы, которые я считаю кластеризацией, а не расщепление. Вот какой код для этого:
FindClusters[Thread[Rule[clusterIndices, points]]]
Я не знаю о "лучше", но более обычным способом в функциональных языках было бы не добавлять индексы для обозначения каждого элемента (вашего MapIndexed), а вместо этого просто запускать по каждому списку:
Map[#1[[2]] &,
Sort[GatherBy[
Thread[ {#1, #2} &[clusterIndices, points]],
#1[[1]] &], #1[[1]][[1]] < #2[[1]][[1]] &], {2}]
Большинство людей, появившихся в Lisp/ML/etc, напишут функцию Thread
сразу, это способ реализовать идеи zip с этих языков.
Я добавил в Sort
, потому что похоже, что ваша реализация столкнется с проблемой, если clusterIndices = {2[...,2],1,...}
. С другой стороны, мне все равно нужно добавить строку, чтобы исправить проблему: если у clusterIndices есть 3, но нет 2, выходные индексы будут ошибочными. Из вашего фрагмента неясно, как вы собираетесь искать вещи.
Я считаю, что вы сможете найти обработку списка намного проще, если обновите себя хобби-проектом, например, создайте простой CAS на языке Haskell, где синтаксис гораздо больше подходит для обработки функциональных списков, чем Mathematica.
Если я подумаю о чем-то более простом, я добавлю к сообщению.
Map[#[[1]] &, GatherBy[Thread[{points, clusterIndices}], #[[2]] &], {2}]
Моим первым шагом было бы выполнить
Transpose[{clusterIndices, points}]
и мой следующий шаг будет зависеть от того, что вы хотите с этим сделать; Select
приходит в голову.