ggalluvial 中如何调整 axis 的矩形顺序?factor levels 不生效
保留 factor levels 的设置。如果不同 axis 中有同名类别,还需要在各图层中统一设置 discern = TRUE,避免它们在内部转换时共用一套顺序。再用 decreasing = NA 按类别排序,并指定排列方向。
以下按从上到下依次显示 0、2、3 和 low、int、high 设置。没有实际数据,还无法确定具体原因,可以先检查不同 axis 是否存在同名类别。
为什么单独设置 factor levels 可能无效?
使用 aes(axis1 = ..., axis2 = ..., ...) 时,ggalluvial 会在内部将宽表转换成长表,把各 axis 的类别合并到一个 stratum 因子中。
默认情况下,不同 axis 中的同名类别不会被区分。合并后的 levels 会按各轴 levels 首次出现的顺序去重。例如,前面的 axis 已经按 3、0、2 引入了这些类别,即使 axis3 的 levels 设为 0、2、3,也可能受合并后顺序的影响。具体逻辑见 to_lodes_form() 的实现。
discern = TRUE 会区分不同 axis 中的同名类别,让各轴保留自己的类别顺序。相应的统计图层可以将该参数传给 to_lodes_form(),参见参数说明。
原代码中的另外两处设置各有用途,都不能直接指定各 axis 内矩形的上下顺序:
order = as.numeric(int_score)控制同一个矩形内部的流线排列,参见order的说明。scale_x_discrete(limits = ...)控制四个 axis 的横向顺序。
在所有图层中统一设置排序参数
先设置 axis3 和 axis4 的 levels,再重新创建图形:
library(ggplot2)
library(ggalluvial)
df$axis3_score <- factor(
df$axis3_score,
levels = c("0", "2", "3")
)
df$axis4_score <- factor(
df$axis4_score,
levels = c("low", "int", "high")
)
p <- ggplot(
df,
aes(
axis1 = axis1_score,
axis2 = axis2_score,
axis3 = axis3_score,
axis4 = axis4_score,
y = total_score
)
) +
geom_flow(
aes(fill = int_score, order = as.numeric(int_score)),
width = 0.2,
discern = TRUE,
decreasing = NA,
reverse = TRUE
) +
geom_stratum(
width = 0.2,
fill = "grey",
discern = TRUE,
decreasing = NA,
reverse = TRUE
) +
geom_text(
stat = "stratum",
aes(label = after_stat(stratum)),
discern = TRUE,
decreasing = NA,
reverse = TRUE
) +
scale_x_discrete(
limits = c("axis1", "axis2", "axis3", "axis4"),
expand = c(0.1, 0.1)
) +
theme_minimal()
p
这几个参数分别控制类别区分、排序依据和显示方向:
discern = TRUE:区分不同 axis 中的同名类别。decreasing = NA:按类别顺序排列,不按矩形总量大小排序。reverse = TRUE:在权重非负、y 轴未反转的通常情况下,让 levels 按从上到下的方向显示。
如果希望指定顺序从下到上显示,将三个图层的 reverse 都改为 FALSE。流线、矩形和文字分别计算位置,排序参数必须一致,否则可能错位。参见排序方向与图层一致性说明。
启用 discern 后,标签出现后缀怎么办?
discern = TRUE 可能将重复类别命名为 "0"、"0.1" 等,因此用 after_stat(stratum) 直接显示标签时,也可能显示这些后缀。
如果需要保留原始标签,可以先显式转换成长表,同时保存原始轴值。以下代码接在前面的 factor 设置之后运行:
axis_cols <- c(
"axis1_score", "axis2_score",
"axis3_score", "axis4_score"
)
lodes <- to_lodes_form(
df,
axes = axis_cols,
key = "axis",
value = "stratum",
id = "alluvium",
discern = TRUE,
diffuse = TRUE
)
# diffuse = TRUE 保留原始轴列,用于恢复显示标签
lodes$stratum_label <- NA_character_
for (nm in axis_cols) {
idx <- as.character(lodes$axis) == nm
lodes$stratum_label[idx] <- as.character(lodes[[nm]][idx])
}
ggplot(
lodes,
aes(
x = axis,
stratum = stratum,
alluvium = alluvium,
y = total_score
)
) +
geom_flow(
aes(fill = int_score, order = as.numeric(int_score)),
width = 0.2,
decreasing = NA,
reverse = TRUE
) +
geom_stratum(
width = 0.2,
fill = "grey",
decreasing = NA,
reverse = TRUE
) +
geom_text(
stat = "stratum",
aes(label = stratum_label),
decreasing = NA,
reverse = TRUE
) +
scale_x_discrete(
limits = axis_cols,
labels = c("axis1", "axis2", "axis3", "axis4"),
expand = c(0.1, 0.1)
) +
theme_minimal()
数据已在绘图前完成转换,图层中不再需要传入 discern。diffuse = TRUE 保留了原始轴列,可用于恢复显示标签;只要每个 stratum 内的原始标签一致,就可以直接用于文字图层。参见数据转换说明和标签映射规则。
如果仍不符合预期,检查实际值和绘图数据
先确认 levels 与数据值是否匹配:
levels(df$axis3_score)
levels(df$axis4_score)
table(df$axis3_score, useNA = "ifany")
table(df$axis4_score, useNA = "ifany")
还需要检查以下情况:
"low "与"low"、"Low"与"low"是不同的值。未列入levels的值会在factor()转换时变成NA,应在转换前核对原始数据。- 修改
df后,要重新运行创建ggplot()对象的代码。只打印之前创建的图对象,可能仍会使用旧数据。 - 某个类别没有观测,或总权重为零时,即使保留了 level,也不会显示有可见高度的矩形。
as.numeric(int_score)对 factor 返回的是内部编码。如果保留这项流线排序,需要确认int_score的 levels 顺序符合预期。
备注:内容仅供参考。
