三角形内的热图
Heatmap within a triangle
考虑以下示例:
triangle_lines <- data.frame(
X = c(0,0,1,1,0.5,0.5),
Y = c(0,0,0,0,1, 1),
grp = c(1,2,1,3,2,3)
)
df <-
matrix(c(c(0.2,0.5,0.8), c(0.3,0.5,0.1), c(1,5,10)), ncol=3) %>%
as.data.frame()
colnames(df) <- c("x","y", "amount")
ggplot(df, aes(x=x, y=y)) +
geom_point(aes(colour = amount)) +
geom_line(data=triangle_lines, aes(X, Y, group = grp)) +
theme_void()
我们有三个值:x
坐标、y
坐标和决定散点颜色值的amount
。这些点之间的距离不等。
另外,我有一个三角形。我正在尝试仅在这个三角形 内部 创建一个渐变热图,用基于 df$amount
的热图颜色填充整个三角形。知道如何解决这个问题吗?
P.S。虽然我在这里只是举例说明三个点,但这个图在实际应用中将有几十个或数百个点。
不是最干净的解决方案,但它有效。
首先我们启动一个空列表。
然后我们创建一系列 lambda 值,每个轴一个,用于在该尺度上的两个点(即 0 和 1 之间)之间创建加权平均值。在此示例中,每个轴有 101 个 lambda,创建 101^2 次迭代。
然后我们检查生成的坐标是否位于 sp::point.in.polygon
的三角形内。请注意,此方法适用于更多形状,而不仅仅是三角形,因此此解决方案适用于多种形状。
如果lambda值生成的坐标在多边形内部,那么我们计算这个坐标到df
中每个坐标的距离。请注意,我们将 sqrt(2)
减去距离,因为距离越小,该点应承载的权重越大。因此,我们取最大距离 (sqrt(2)
) 并减去距离。 sqrt(2)
不是必须设置的确定数字,但它确实可以防止出现负值。其他值提供其他结果。
在下一步中,我们缩放距离,使它们的总和为 1。这样我们就可以创建一个加权平均值,它在 amount
中定义。
在 运行 循环之后,我们将列表绑定到数据框中并创建绘图。
为了确保边缘光滑,我们制作了一些带有圆边的粗白线。
gradient_list <- list()
for (lambda_x in seq(0,1,by=0.01)) {
for (lambda_y in seq(0,1,by=0.01)) {
x_value <- lambda_x*0 + (1-lambda_x)*1
y_value <- lambda_y*0 + (1-lambda_y)*1
inside_polygon <- sp::point.in.polygon(x_value, y_value, triangle_lines$X, triangle_lines$Y) %>% as.logical()
if (inside_polygon) {
point <- c(x_value, y_value)
distances <- sqrt(2) - sqrt((df$x - point[1])^2 + (df$y - point[2])^2)
weighted_distances <- distances/sum(distances)
amount <- sum(weighted_distances * df$z)
gradient_list <- append(gradient_list, list(c(point, amount)))
}
}
}
gradient_df <- do.call(rbind, gradient_list) %>% as.data.frame()
colnames(gradient_df) <- c("x","y","amount")
ggplot(gradient_df, aes(x=x, y=y)) +
geom_point(aes(colour = amount), size=2) +
theme_void() +
geom_line(data=triangle_lines, aes(X, Y, group = grp), size=3, colour="white", lineend="round")
考虑以下示例:
triangle_lines <- data.frame(
X = c(0,0,1,1,0.5,0.5),
Y = c(0,0,0,0,1, 1),
grp = c(1,2,1,3,2,3)
)
df <-
matrix(c(c(0.2,0.5,0.8), c(0.3,0.5,0.1), c(1,5,10)), ncol=3) %>%
as.data.frame()
colnames(df) <- c("x","y", "amount")
ggplot(df, aes(x=x, y=y)) +
geom_point(aes(colour = amount)) +
geom_line(data=triangle_lines, aes(X, Y, group = grp)) +
theme_void()
我们有三个值:x
坐标、y
坐标和决定散点颜色值的amount
。这些点之间的距离不等。
另外,我有一个三角形。我正在尝试仅在这个三角形 内部 创建一个渐变热图,用基于 df$amount
的热图颜色填充整个三角形。知道如何解决这个问题吗?
P.S。虽然我在这里只是举例说明三个点,但这个图在实际应用中将有几十个或数百个点。
不是最干净的解决方案,但它有效。
首先我们启动一个空列表。
然后我们创建一系列 lambda 值,每个轴一个,用于在该尺度上的两个点(即 0 和 1 之间)之间创建加权平均值。在此示例中,每个轴有 101 个 lambda,创建 101^2 次迭代。
然后我们检查生成的坐标是否位于 sp::point.in.polygon
的三角形内。请注意,此方法适用于更多形状,而不仅仅是三角形,因此此解决方案适用于多种形状。
如果lambda值生成的坐标在多边形内部,那么我们计算这个坐标到df
中每个坐标的距离。请注意,我们将 sqrt(2)
减去距离,因为距离越小,该点应承载的权重越大。因此,我们取最大距离 (sqrt(2)
) 并减去距离。 sqrt(2)
不是必须设置的确定数字,但它确实可以防止出现负值。其他值提供其他结果。
在下一步中,我们缩放距离,使它们的总和为 1。这样我们就可以创建一个加权平均值,它在 amount
中定义。
在 运行 循环之后,我们将列表绑定到数据框中并创建绘图。
为了确保边缘光滑,我们制作了一些带有圆边的粗白线。
gradient_list <- list()
for (lambda_x in seq(0,1,by=0.01)) {
for (lambda_y in seq(0,1,by=0.01)) {
x_value <- lambda_x*0 + (1-lambda_x)*1
y_value <- lambda_y*0 + (1-lambda_y)*1
inside_polygon <- sp::point.in.polygon(x_value, y_value, triangle_lines$X, triangle_lines$Y) %>% as.logical()
if (inside_polygon) {
point <- c(x_value, y_value)
distances <- sqrt(2) - sqrt((df$x - point[1])^2 + (df$y - point[2])^2)
weighted_distances <- distances/sum(distances)
amount <- sum(weighted_distances * df$z)
gradient_list <- append(gradient_list, list(c(point, amount)))
}
}
}
gradient_df <- do.call(rbind, gradient_list) %>% as.data.frame()
colnames(gradient_df) <- c("x","y","amount")
ggplot(gradient_df, aes(x=x, y=y)) +
geom_point(aes(colour = amount), size=2) +
theme_void() +
geom_line(data=triangle_lines, aes(X, Y, group = grp), size=3, colour="white", lineend="round")