Назначьте случайные цвета в R

У меня есть этот код на R для моделирования страны с разными районами:

library(igraph)

width <- 30
height <- 20
num_nodes <- width * height

x <- rep(1:width, each = height)
y <- rep(1:height, times = width)

g <- make_empty_graph(n = num_nodes, directed = FALSE)

get_node_index <- function(i, j) (i - 1) * height + j

# Add edges
edges <- c()
for(i in 1:width) {
  for(j in 1:height) {
    current_node <- get_node_index(i, j)
    if (i < width) edges <- c(edges, current_node, get_node_index(i + 1, j))
    if (j < height) edges <- c(edges, current_node, get_node_index(i, j + 1))
  }
}
g <- add_edges(g, edges)

V(g)$x <- x
V(g)$y <- y

V(g)$color <- sample(c("red", "blue"), num_nodes, replace = TRUE)

count_patches <- function(color) {
  subgraph <- induced_subgraph(g, V(g)[V(g)$color == color])
  components <- components(subgraph)
  return(components$no)
}

plot(g, layout = cbind(V(g)$x, V(g)$y), 
     vertex.size = 7,  
     vertex.label = NA,
     edge.arrow.size = 0.5,
     edge.color = "lightgray"
     )

Однако это просто присвоение случайных цветов каждому узлу.

Я пытаюсь объединить цвета в «кластеры», чтобы они напоминали более естественный узор, вот так:

https://commons.wikimedia.org/wiki/Category:Texas_gubernatory_election_maps#/media/File:TXGov1990Map.png

Можно ли это сделать в R?


Вторая попытка:

library(igraph)

width <- 30
height <- 20
num_nodes <- width * height

x <- rep(1:width, each = height)
y <- rep(1:height, times = width)

g <- make_empty_graph(n = num_nodes, directed = FALSE)

#  get node index
get_node_index <- function(i, j) (i - 1) * height + j

# add edges
edges <- c()
for(i in 1:width) {
    for(j in 1:height) {
        current_node <- get_node_index(i, j)
        # Connect to right neighbor
        if (i < width) edges <- c(edges, current_node, get_node_index(i + 1, j))
        # Connect to bottom neighbor
        if (j < height) edges <- c(edges, current_node, get_node_index(i, j + 1))
    }
}
g <- add_edges(g, edges)

# set node positions
V(g)$x <- x
V(g)$y <- y

# initialize all nodes as white
V(g)$color <- "white"

#  get neighbors
get_neighbors <- function(node) {
    neighbors(g, node)
}

#define seeds
num_seeds <- 50
red_seeds <- sample(V(g), num_seeds)
blue_seeds <- sample(setdiff(V(g), red_seeds), num_seeds)

# color the seed nodes
V(g)[red_seeds]$color <- "red"
V(g)[blue_seeds]$color <- "blue"

#  initial probability for color spreading
base_spread_probability <- 0.2

# diffusion process
while(any(V(g)$color == "white")) {
    red_front <- V(g)[V(g)$color == "red"]
    blue_front <- V(g)[V(g)$color == "blue"]
    
    new_red <- unique(unlist(sapply(red_front, get_neighbors)))
    new_blue <- unique(unlist(sapply(blue_front, get_neighbors)))
    
    # color new nodes with probability, but don't overwrite existing colors
    for (node in new_red[V(g)[new_red]$color == "white"]) {
        if (runif (1) < base_spread_probability * (1 + runif (1, -0.5, 0.5))) {
            V(g)[node]$color <- "red"
        }
    }
    
    for (node in new_blue[V(g)[new_blue]$color == "white"]) {
        if (runif (1) < base_spread_probability * (1 + runif (1, -0.5, 0.5))) {
            V(g)[node]$color <- "blue"
        }
    }
    
    # If no new nodes were colored, increase probability to ensure completion
    if (all(V(g)[new_red]$color != "red" & V(g)[new_blue]$color != "blue")) {
        base_spread_probability <- min(1, base_spread_probability + 0.05)
    }
}


plot(g, layout = cbind(V(g)$x, V(g)$y), 
     vertex.size = 7, 
     vertex.label = NA, 
     edge.arrow.size = 0.5)

Ознакомьтесь с пакетом spatstat для получения пространственной статистики. У него есть много способов выборки объектов, которые пространственно сгруппированы. Или сделайте свой собственный выбор, выберите несколько точек в качестве случайных стартов, а затем создайте итерационный процесс, в котором каждой неокрашенной точке с цветными соседями присваивается цвет с вероятностью, основанной на ее смежности. Или определите непрерывную функцию для области и определите цвета на основе значения этой функции с добавлением шума...

Gregor Thomas 04.09.2024 02:53

не могли бы вы показать мне пример, если у вас есть время?

farrow90 04.09.2024 03:02

Должны ли красные и синие иметь соотношение 50/50?

jpsmith 04.09.2024 03:08

@jpsmith: любая пропорция подойдет...

farrow90 04.09.2024 16:31
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
99
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Не совсем понятно, чего вы пытаетесь достичь, поэтому я не уверен, поможет ли это, но вот действительно грубое решение, которое случайным образом назначает цвет в кластерах графику в вашем примере.

library(igraph)
#> 
#> Attaching package: 'igraph'
#> The following objects are masked from 'package:stats':
#> 
#>     decompose, spectrum
#> The following object is masked from 'package:base':
#> 
#>     union

width <- 30
height <- 20
num_nodes <- width * height

x <- rep(1:width, each = height)
y <- rep(1:height, times = width)

g <- make_empty_graph(n = num_nodes, directed = FALSE)

get_node_index <- function(i, j) (i - 1) * height + j

# Add edges
edges <- c()
for(i in 1:width) {
  for(j in 1:height) {
    current_node <- get_node_index(i, j)
    if (i < width) edges <- c(edges, current_node, get_node_index(i + 1, j))
    if (j < height) edges <- c(edges, current_node, get_node_index(i, j + 1))
  }
}
g <- add_edges(g, edges)

V(g)$x <- x
V(g)$y <- y

colors <- NULL
i <- 1
while(i <= num_nodes){
  # Set a size for the cluster randomly
  cluster_size <- sample(seq(5, 50), 1)
  # Set the color for the cluster randomly
  cluster_color <- sample(c("red", "blue"), 1, replace = TRUE)
  # Assign the cluster color for each node in the cluster or as many that remain in the graph
  if ((i + cluster_size - 1) <= num_nodes){
    cluster_colors <- sample(cluster_color, cluster_size, replace = TRUE)
  } else {
    cluster_colors <- sample(cluster_color, num_nodes - i + 1, replace = TRUE)
  }
  colors <- append(colors, cluster_colors)
  i <- i + cluster_size
}
V(g)$color <- colors

count_patches <- function(color) {
  subgraph <- induced_subgraph(g, V(g)[V(g)$color == color])
  components <- components(subgraph)
  return(components$no)
}

plot(g, layout = cbind(V(g)$x, V(g)$y), 
     vertex.size = 7,  
     vertex.label = NA,
     edge.arrow.size = 0.5,
     edge.color = "lightgray"
)

Created on 2024-09-04 with reprex v2.1.1

большое спасибо! можно ли сделать узоры более "пятнистыми"? например острова красного и синего цвета, напоминающие округа в штате?

farrow90 04.09.2024 16:33

Это интересный вопрос, и я уверен, что есть несколько способов подойти к нему. Одним из способов было бы создать функцию, которая случайным образом создает n кластерных «семен» (nclust), расширяет их до заданного размера (clustsize), а затем случайным образом равномерно подвергает их цензуре, чтобы создать «неоднородность» (censoring):

set.seed(123)
clust_fun <- function(v_g, 
                      nclust = 10, 
                      clustsize = 20, 
                      censoring = 0.1){
  clust_seed <- sort(sample(length(v_g), nclust))
  xx <- unique(unlist(purrr::map2(clust_seed - (clustsize / 2), clust_seed + (clustsize / 2), seq)))
  xx[runif (length(xx)) <= (1 - censoring) & xx > 0 & xx < length(v_g)]
}

nn <- length(V(g))
V(g)$color <- rep("blue", nn)
V(g)$color[clust_fun(v_g = V(g))] <- "red"

plot(g, layout = cbind(V(g)$x, V(g)$y), 
     vertex.size = 7,  
     vertex.label = NA,
     edge.arrow.size = 0.5,
     edge.color = "lightgray"
)

Здесь я по умолчанию выбрал 10 семян с максимальным размером кластера 20 с небольшой «пятнистостью» (10%). Вы можете поиграть, чтобы сделать их более точными в соответствии с тем, что вы себе представляете.

jpsmith, спасибо большое! я опубликовал обновленную информацию о своем собственном прогрессе...

farrow90 04.09.2024 16:52
Ответ принят как подходящий

Одним из простых вариантов является применение ядра сглаживания к матричному/растровому представлению числовых значений сетки, для этого мы можем использовать terra::focal().

В качестве первого шага мы построим SpatRaster для terra из таблицы вершин графика (x, y, color), затем мы сможем использовать focal(fun = "mean", ...), который по умолчанию использует скользящее окно 3x3 для расчета средних значений для каждой ячейки. Установив пороговые значения/сгруппировав средние значения, мы можем вернуться к категориальным показателям, таким как цвета.

Чтобы настроить полученные шаблоны, можно применить несколько раз focal() и отрегулировать пороговое значение. Мы также можем изменить вектор вероятности в sample(), чтобы изменить соотношение красного и синего входных данных.

library(igraph)
library(terra)

width <- 30
height <- 20
num_nodes <- width * height

x <- rep(1:width, each = height)
y <- rep(1:height, times = width)
g <- make_lattice(c(height,width))
V(g)$x <- x
V(g)$y <- y

set.seed(42)
V(g)$color <- sample(c("red", "blue"), num_nodes, replace = TRUE)

# helper function to apply mean filter (smoothing 3x3 kernel) to terra SpatRaster r, 
# repeat n times
mean_n <- \(r, n) Reduce(\(x, ...) focal(x, fun = "mean", na.rm = TRUE, expand = TRUE), x = seq_len(n), init = r)

V(g)$color <- 
  as_data_frame(g, "vertices") |> 
  # recode colors to numericals
  within(color <- c("red" = 0, "blue" = 1)[color]) |> 
  # vertex frame (x, y, numerical color) to SpatRaster
  rast() |>
  # apply focal(r, fun = "mean") twice
  mean_n(2) |> 
  # use .5 threshold value to categorize back to "red" & "blue";
  # from factor to character for plotting
  as.matrix(wide = TRUE) |> 
  cut(breaks = c(-Inf, .5, Inf), labels = c("red","blue")) |> 
  as.character() 

withr::with_par(
  list(mar = c(0,0,0,0)),
  plot(g, layout = cbind(V(g)$x, V(g)$y), 
       vertex.size = 7,  
       vertex.label = NA,
       edge.arrow.size = 0.5,
       edge.color = "lightgray"
       )
)

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

спасибо @margusl! Я пытался сделать это другим методом здесь stackoverflow.com/questions/78949381/…

farrow90 04.09.2024 19:06

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