我想使用不同的布局为同一个网络设置动画,并使布局之间平滑过渡。我想在gganimate
框架内进行此操作。
library(igraph)
library(ggraph)
library(gganimate)
set.seed(1)
g <- erdos.renyi.game(10, .5, "gnp")
V(g)$name <- letters[1:vcount(g)]
l1 <- create_layout(g, "kk")
l2 <- create_layout(g, "circle")
l3 <- create_layout(g, "nicely")
long <- rbind(l1,l2,l3)
long$frame <- rep(1:3, each =10)
按照ggplot
方法,我以长格式(long
)存储节点位置,并向每个布局添加一个frame
变量。
我试图使其与下面的代码一起使用,它可以正常工作并且几乎是我想要的。但是,我似乎找不到找到包括边缘的方法:
ggplot(long, aes(x, y, label = name, color = as.factor(name), frame = frame)) +
geom_point(size = 3, show.legend = F) +
geom_text(show.legend = F) +
transition_components(frame)
我还尝试将边缘添加为geom_segment
,但最终在节点保持移动的同时使它们变为静态。这就是为什么我使用ggraph
程序包并失败的原因:
ggraph(g, layout = "manual", node.position = long) +
geom_node_point() +
geom_edge_link() +
transition_components(frame)
我想制作一个动画,其中一个网络的节点位置发生变化,同时显示节点和边缘。
非常感谢您的帮助!
编辑:我了解到可以将布局直接包含在ggraph中(甚至可以操纵属性)。这就是我在以下gif中所做的。另外,正在使用geom_edge_link0'
而不是geom_edge_link
。
ggraph(long) +
geom_edge_link0() +
geom_node_point() +
transition_states(frame)
请注意,边缘没有移动。
答案 0 :(得分:3)
我不确定gganimate
中当前是否已准备就绪。截至2019年5月,这似乎是一个相关问题:https://github.com/thomasp85/gganimate/issues/139
编辑我已替换为有效的解决方案。公平的警告,我是网络操作的新手,我希望有更多经验的人可以将代码重构得短得多。
我的一般方法是创建布局,将节点放入表long2
中,然后创建具有所有边缘的另一个表。 gganimate
然后调用每一层需要的相应数据源。
set.seed(1)
g <- erdos.renyi.game(10, .5, "gnp")
V(g)$name <- letters[1:vcount(g)]
layouts <- c("kk", "circle", "nicely")
long2 <- lapply(layouts, create_layout, graph = g) %>%
enframe(name = "frame") %>%
unnest()
> head(long2)
# A tibble: 6 x 7
frame x y name ggraph.orig_index circular ggraph.index
<int> <dbl> <dbl> <fct> <int> <lgl> <int>
1 1 -1.07 0.363 a 1 FALSE 1
2 1 1.06 0.160 b 2 FALSE 2
3 1 -1.69 -0.310 c 3 FALSE 3
4 1 -0.481 0.135 d 4 FALSE 4
5 1 -0.0603 -0.496 e 5 FALSE 5
6 1 0.0373 1.02 f 6 FALSE 6
在这里,我从g
中提取边缘,并将其整形为geom_segment
可以使用的格式,并为x
,y
,xend
和yend
。重构已经成熟,但是可以正常工作。
edges_df <- igraph::as_data_frame(g, "edges") %>%
tibble::rowid_to_column() %>%
gather(end, name, -rowid) %>%
# Here we get the coordinates for each node from `long2`.
left_join(long2 %>% select(frame, name, x, y)) %>%
gather(coord, val, x:y) %>%
# create xend and yend when at the "to" end, for geom_segment use later
mutate(col = paste0(coord, if_else(end == "to", "end", ""))) %>%
select(frame, rowid, col, val) %>%
arrange(frame, rowid) %>%
spread(col, val) %>%
# Get the node names for the coordinates we're using, so that we
# can name the edge from a to b as "a_b" and gganimate can tween
# correctly between frames.
left_join(long2 %>% select(frame, x, y, start_name = name)) %>%
left_join(long2 %>% select(frame, xend = x, yend = y, end_name = name)) %>%
unite(edge_name, c("start_name", "end_name"))
> head(edges_df)
frame rowid x xend y yend edge_name
1 1 1 -1.0709480 -1.69252646 0.3630563 -0.3095612 a_c
2 1 2 -1.0709480 -0.48086213 0.3630563 0.1353664 a_d
3 1 3 -1.6925265 -0.48086213 -0.3095612 0.1353664 c_d
4 1 4 -1.0709480 -0.06032354 0.3630563 -0.4957609 a_e
5 1 5 1.0571895 -0.06032354 0.1596417 -0.4957609 b_e
6 1 6 -0.4808621 -0.06032354 0.1353664 -0.4957609 d_e
ggplot() +
geom_segment(data = edges_df,
aes(x = x, xend = xend, y = y, yend = yend, color = edge_name)) +
geom_point(data = long2, aes(x, y, color = name), size = 4) +
geom_text(data = long2, aes(x, y, label = name)) +
guides(color = F) +
ease_aes("quadratic-in-out") +
transition_states(frame, state_length = 0.5) -> a
animate(a, nframes = 400, fps = 30, width = 700, height = 300)