У меня есть набор данных в следующем формате:
heights <- rnorm(10000, mean=170, sd=10)
weights <- rnorm(10000, mean=65, sd=15)
data <- data.frame(heights, weights)
heights weights
1 164.0554 75.21385
2 167.8416 80.20245
3 170.8382 64.86342
4 175.3897 73.40080
5 177.6491 42.82188
6 169.2133 79.28145
Я использовал следующий код R, чтобы подсчитать, сколько точек данных содержится в каждом интервале размером 5x5:
max_height <- max(data$heights)
max_weight <- max(data$weights)
height_breaks <- seq(0, max_height, by=5)
weight_breaks <- seq(0, max_weight, by=5)
combinations <- expand.grid(height = seq_along(height_breaks)[-length(height_breaks)],
weight = seq_along(weight_breaks)[-length(weight_breaks)])
interval_label <- function(breaks, index) {
paste0("(", breaks[index], "-", breaks[index + 1], ")")
}
combinations$height_interval <- mapply(interval_label, list(height_breaks), combinations$height)
combinations$weight_interval <- mapply(interval_label, list(weight_breaks), combinations$weight)
height_weight_boxes <- combinations[, c("height_interval", "weight_interval")]
count_points_in_box <- function(min_height, max_height, min_weight, max_weight, data) {
data %>%
filter(height >= min_height, height <= max_height,
weight >= min_weight, weight <= max_weight) %>%
nrow()
}
library(dplyr)
transformed_df <- height_weight_boxes %>%
mutate(
box_number = row_number(),
min_height = as.numeric(sub("\\((.*?)-.*", "\\1", height_interval)),
max_height = as.numeric(sub(".*-(.*)\\)", "\\1", height_interval)),
min_weight = as.numeric(sub("\\((.*?)-.*", "\\1", weight_interval)),
max_weight = as.numeric(sub(".*-(.*)\\)", "\\1", weight_interval))
)
count_points_in_box <- function(min_height, max_height, min_weight, max_weight, data) {
data %>%
filter(heights >= min_height, heights < max_height,
weights >= min_weight, weights < max_weight) %>%
nrow()
}
final <- transformed_df %>%
rowwise() %>%
mutate(count = count_points_in_box(min_height, max_height, min_weight, max_weight, data))
Отсюда я хочу сделать тепловую карту этих данных:
library(ggplot2)
library(viridisLite)
distinct_counts <- length(unique(final$count))
color_palette <- magma(distinct_counts)
final$color <- factor(final$count)
ggplot(final, aes(x = min_weight, y = min_height, fill = final$color)) +
geom_tile() +
scale_fill_manual(values = color_palette, guide = guide_legend(title = "Count")) +
labs(x = "Minimum Weight", y = "Minimum Height", title = "2D Heatmap of Counts") +
theme_minimal() +
theme(legend.position = "right")
Все выглядит хорошо, но кажется, что в легенде показаны все возможные комбинации цветов, и она выглядит очень длинной. Я пытаюсь уменьшить размер легенды и сделать ее более компактной. Можно ли уменьшить размер этой легенды, «объединив» цвета в одинаковых диапазонах?
Я знаю, что можно напрямую создать тепловую карту в R, используя такие функции, как kde(). Проблема в том, что мой реальный набор данных очень велик, и я фактически использую инструкции SQL CASE WHEN для выполнения подсчетов, а затем переношу агрегированный набор данных в R для создания тепловой карты (т. е. «окончательной»). Отсюда я вручную назначаю цвета и создаю тепловую карту. В этом вопросе, который я задал, длительная манипуляция с данными, которую я включил выше, представляет собой манипуляцию с данными SQL для приведения данных в «окончательный» формат.
Генерация операторов SQL в R:
sql_query <- "SELECT *, CASE"
for (i in 1:nrow(df)) {
sql_query <- paste0(sql_query, " WHEN height BETWEEN ", df$min_height[i], " AND ", df$max_height[i],
" AND weight BETWEEN ", df$min_weight[i], " AND ", df$max_weight[i],
" THEN ", df$box_number[i])
}
sql_query <- paste0(sql_query, " END AS box_num FROM my_table;")
Ответы находятся в разделе ответов ниже. Все посты должны быть в актуальном формате и не содержать лишнего шума.





Scale_fill_binned() — это своего рода удобная и не очень гибкая функция, но вы можете использовать use Scale_fill_stepsn() для создания разделенных легенд:
# final$color <- factor(final$count)
breaks <- seq(min(final$count), max(final$count), length.out = 9)
n_breaks <- length(breaks)
pal <- magma(n_breaks)
ggplot(final, aes(x = min_weight, y = min_height, fill = count)) +
geom_tile() +
# scale_fill_manual(values = color_palette, guide = guide_legend(title = "Count")) +
labs(x = "Minimum Weight", y = "Minimum Height", title = "2D Heatmap of Counts") +
scale_x_continuous(breaks = weight_breaks, labels = weight_breaks) +
theme_minimal() +
theme(legend.position = "right")+
scale_fill_stepsn(breaks = breaks, colors = pal, values = scales::rescale(breaks, c(0, 1)))
Вместо того, чтобы конвертировать ваши значения в коэффициент, вы можете рассматривать их как числовые, то есть использовать
fill = count. Затем используйте непрерывную цветовую палитру viridis, например.ggplot2::scale_fill_viridis_c(option = "magma")