在制作GIF时,Chorddiagram(圆圈)中的轴线限制

发布于 2025-01-25 16:13:40 字数 4797 浏览 3 评论 0原文

我希望有人能够帮助我进行我想创建的Chorddiagram可视化。我很清楚,也许这种可视化类型不适合此特定数据,但是以某种方式是我脑海中拥有的东西(或我想如何可视化这些数据)以及我想创建的东西,现在我认为是太晚了,不能放弃它::)太好奇了如何修复它。尽管我是Stackoverflow的活跃用户,但这是我在这里的第一篇真实帖子,我真的很欣赏观众。

因此,我有有关KM2随时间变化(D0)的区域大小的变化的数据(D0),我试图在此处使用示例从中创建一个GIF: https://guyabel.com/post/post/animated-directional-chord-diagrams/

数据“ D0”:

Time <- as.numeric(c(10,10,10,100,100,100,200,200,200,5,5,5,50,50,50,0,0,0))
Year <- as.character(c(2050,2100,2200,2050,2100,2200,2050,2100,2200,2050,2100,2200,2050,2100,2200,2050,2100,2200))
Area_km2 <- as.numeric(c(4.3075211,7.1672926,17.2780622,5.9099250,8.2909189,16.9748961,6.5400554,8.9036313,16.5627228,3.0765610,6.3929883,18.0708108,5.3520782,8.4503856,16.7938196,0.5565978,1.8415855,12.5089476))

(d0 <- as.data.frame(cbind(Time,Year,Area_km2)))

我也有颜色代码按照上述示例存储在单独的数据帧(D1)中。 数据“ D1”:

year <- as.numeric(c(2050,2100,2200))
order1 <- as.character(c(1,2,3))
col1 <- c("#40A4D8","#33BEB7","#0C5BCE")

(d1 <- as.data.frame(cbind(year,order1,col1)))

因此,想法是在每个扇区内的自我链接流动随着时间的变化而增加,这看起来像是在最终动画GIF(或类似于成长的派段)中的片段,但我注意到这一点,无论如何我的努力如何,我似乎无法设法将每个段的轴限制为在每个帧中的特定年限。似乎正在添加轴并不断增加随着时间的推移,这不是我想要的。

例如,在第一个图(图0)或“启动帧”中,链接的大小与dataframe匹配:

figue0

所以它是

orig_yearabreat_km2.frame
20500.5570
21001.840
220012.50

但是当一个图下图(图1)时,轴似乎已从启动帧中取出值并在当前值上添加(分别为4、7.4和19),而不是(3.08、6.39和18.1),或者根据数据框架应该是值:

figue1

orig_yearabreation_km2.frame
20503.081
21006.391
220018.11

,它继续这样做,因为一个循环通过数据循环并为下一帧创建了新的图。我想知道是否有可能以链接随时间逐渐增加,并且轴是随着增加而逐渐增加的方式来限制轴并创建可视化。

任何帮助都非常感谢! 谢谢。

我的代码:

排序减少

(d0 <- arrange(d0,Time))

副本列

(d0$Dest_year <- d0$Year)

重新安排数据

library(tweenr)
(d2 <- d0 %>%
mutate(corridor=paste(Year,Dest_year,sep="->")) %>%
dplyr::select(Time,corridor,Area_km2) %>%
mutate(ease="linear") %>%
tweenr::tween_elements('Time','corridor','ease',nframes=30) %>%
tibble::as_tibble())

(d2 <- d2 %>%
separate(col=.group,into=c("orig_year","dest_year"),sep="->") %>%
dplyr::select(orig_year,dest_year,Area_km2,everything()))

d2$Time <- NULL

创建一个目录,以存储单个图

dir.create("./plot-gif/")

固定量表

scale_gap <- function(Area_km2_m,Area_km2_max,gap_at_max=1,gaps=NULL) {
p <- Area_km2_m/Area_km2_max
if(length(gap_at_max)==1 & !is.null(gaps)) {
gap_at_max <- rep(gap_at_max,gaps)
}
gap_degree <- (360-sum(gap_at_max))*(1-p)
gap_m <- (gap_degree + sum(gap_at_max))/gaps
return(gap_m)
}

功能的单个图,以得出每个帧中的差距大小的动画gif

(d3 <- d2 %>% group_by(orig_year) %>% mutate(gaps=scale_gap(Area_km2_m=Area_km2,Area_km2_max=max(.$Area_km2),gap_at_max=4,gaps=9)))

library(magrittr)

获取轴限制的值,

(axmax <- d2 %>% group_by(orig_year,.frame) %>% mutate(max=mean(Area_km2)))

为每个帧创建唯一的chorddiagram

library(circlize)
for(f in unique(d2$.frame)){
png(file=paste0("./plot-gif/figure",f,".png"),height=7,width=7,units="in",res=500)
circos.clear()
par(mar=rep(0,4),cex=1)
circos.par(start.degree=90,track.margin=c(-0.1,0.1),
gap.degree=filter(d3,.frame==f)$gaps,
points.overflow.warning=FALSE)
chordDiagram(x=filter(d2,.frame==f),directional=2,order=d1$year,
grid.col=d1$col1,annotationTrack=c("grid","name","axis"),
transparency=0.25,annotationTrackHeight=c(0.05,0.1),
direction.type=c("diffHeight"),
diffHeight=-0.04,link.sort=TRUE,
xmax=axmax$max)
dev.off()
}
    

,现在GIF

library(magick)
img <- image_read(path="./plot-gif/figure0.png")
for(f in unique(d2$.frame)[-1]){
img0 <- image_read(path=paste0("./plot-gif/figure",f,".png"))
img <- c(img,img0)
message(f)
}
img1 <- image_scale(image=img,geometry="720x720")
ani0 <- image_animate(image=img1,fps=10)
image_write(image=ani0,path="./plot-gif/figure.gif")

I hope somebody will be able to help me with this chordDiagram visualisation I am trying to create. I am well aware that maybe this visualization type was not suitable for this particular data, but somehow it was something I had in my head (or how I wanted to visualize this data) and what I wanted to create, and now I think it is too late to give it up :) too curious how one can fix it. It is my first real post here, though I am an active user of stackoverflow and I genuinely admire the audience here.

So I have this data on the change in the size of area in km2 over time (d0) and I am trying to create a GIF out of it using example here: https://guyabel.com/post/animated-directional-chord-diagrams/

The data "d0":

Time <- as.numeric(c(10,10,10,100,100,100,200,200,200,5,5,5,50,50,50,0,0,0))
Year <- as.character(c(2050,2100,2200,2050,2100,2200,2050,2100,2200,2050,2100,2200,2050,2100,2200,2050,2100,2200))
Area_km2 <- as.numeric(c(4.3075211,7.1672926,17.2780622,5.9099250,8.2909189,16.9748961,6.5400554,8.9036313,16.5627228,3.0765610,6.3929883,18.0708108,5.3520782,8.4503856,16.7938196,0.5565978,1.8415855,12.5089476))

(d0 <- as.data.frame(cbind(Time,Year,Area_km2)))

I also have the color codes stored in a separate dataframe (d1) following the above mentioned example.
The data "d1":

year <- as.numeric(c(2050,2100,2200))
order1 <- as.character(c(1,2,3))
col1 <- c("#40A4D8","#33BEB7","#0C5BCE")

(d1 <- as.data.frame(cbind(year,order1,col1)))

So the idea was to have self-linking flows within each sector increasing in size over time, which will look like just growing segments in a final animated GIF (or like growing pie segments), but I noticed that regardless how hard I try I can't seem to manage to constrain the axis of each segment to limits of that particular year in an every single frame. It seems that the axis is being added on and keeps on adding over time, which is not what I want.

Like for example in the first figure (figure0) or "starting frame" the size of the links matches well the dataframe:

figure0

So it is

orig_yearArea_km2.frame
20500.5570
21001.840
220012.50

But when one plots next figure (figure1), the axis seems to have taken the values from the starting frame and added on the current values (4, 7.4 and 19 respectively) instead of (3.08, 6.39 and 18.1) or what should have been the values according the data frame:

figure1

orig_yearArea_km2.frame
20503.081
21006.391
220018.11

And it keep on doing so as one loops through the data and creates new plots for the next frames. I wonder whether it is possible to constrain the axis and create the visualization in a way that the links just gradually increase over time and the axis is, so to say, following the increase or does also increase gradually following the data???

Any help is highly appreciated!
Thanks.

My code:

Sort decreasing

(d0 <- arrange(d0,Time))

Copy columns

(d0$Dest_year <- d0$Year)

Re-arrange data

library(tweenr)
(d2 <- d0 %>%
mutate(corridor=paste(Year,Dest_year,sep="->")) %>%
dplyr::select(Time,corridor,Area_km2) %>%
mutate(ease="linear") %>%
tweenr::tween_elements('Time','corridor','ease',nframes=30) %>%
tibble::as_tibble())

(d2 <- d2 %>%
separate(col=.group,into=c("orig_year","dest_year"),sep="->") %>%
dplyr::select(orig_year,dest_year,Area_km2,everything()))

d2$Time <- NULL

Create a directory to store the individual plots

dir.create("./plot-gif/")

Fixing scales

scale_gap <- function(Area_km2_m,Area_km2_max,gap_at_max=1,gaps=NULL) {
p <- Area_km2_m/Area_km2_max
if(length(gap_at_max)==1 & !is.null(gaps)) {
gap_at_max <- rep(gap_at_max,gaps)
}
gap_degree <- (360-sum(gap_at_max))*(1-p)
gap_m <- (gap_degree + sum(gap_at_max))/gaps
return(gap_m)
}

Function to derive the size of gaps in each frame for an animated GIF

(d3 <- d2 %>% group_by(orig_year) %>% mutate(gaps=scale_gap(Area_km2_m=Area_km2,Area_km2_max=max(.$Area_km2),gap_at_max=4,gaps=9)))

library(magrittr)

Get the values for axis limits

(axmax <- d2 %>% group_by(orig_year,.frame) %>% mutate(max=mean(Area_km2)))

Creating unique chordDiagrams for each frame

library(circlize)
for(f in unique(d2$.frame)){
png(file=paste0("./plot-gif/figure",f,".png"),height=7,width=7,units="in",res=500)
circos.clear()
par(mar=rep(0,4),cex=1)
circos.par(start.degree=90,track.margin=c(-0.1,0.1),
gap.degree=filter(d3,.frame==f)$gaps,
points.overflow.warning=FALSE)
chordDiagram(x=filter(d2,.frame==f),directional=2,order=d1$year,
grid.col=d1$col1,annotationTrack=c("grid","name","axis"),
transparency=0.25,annotationTrackHeight=c(0.05,0.1),
direction.type=c("diffHeight"),
diffHeight=-0.04,link.sort=TRUE,
xmax=axmax$max)
dev.off()
}
    

Now make a GIF

library(magick)
img <- image_read(path="./plot-gif/figure0.png")
for(f in unique(d2$.frame)[-1]){
img0 <- image_read(path=paste0("./plot-gif/figure",f,".png"))
img <- c(img,img0)
message(f)
}
img1 <- image_scale(image=img,geometry="720x720")
ani0 <- image_animate(image=img1,fps=10)
image_write(image=ani0,path="./plot-gif/figure.gif")

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

秋心╮凉 2025-02-01 16:13:40

我将从您的d0对象开始。我首先构造d0对象,但我不将所有内容转换为字符,只将它们作为原始数字格式。另外,我还通过时间 year 重新排序d0

Time = c(10,10,10,100,100,100,200,200,200,5,5,5,50,50,50,0,0,0)
Year = c(2050,2100,2200,2050,2100,2200,2050,2100,2200,2050,2100,2200,2050,2100,2200,2050,2100,2200)
Area_km2 = c(4.3075211,7.1672926,17.2780622,5.9099250,8.2909189,16.9748961,6.5400554,8.9036313,16.5627228,3.0765610,6.3929883,18.0708108,5.3520782,8.4503856,16.7938196,0.5565978,1.8415855,12.5089476)

d0 = data.frame(Time = Time,
    Year = Year,
    Area_km2 = Area_km2,
    Dest_year = Year)

d0 = d0[order(d0$Time, d0$Year), ]

关键是为部门之间的“差距”计算正确的值,以便从数据对应于不同图中的相同程度。

我们首先计算圆形图的最大总宽度:

width = tapply(d0$Area_km2, d0$Time, sum)
max_width = max(width)

我们假设有n个部门(其中n = 3 in d0)。我们让第一个N-1间隙为2度,并根据每个图中的总值动态调整最后一个差距。对于最大总值的图,最后一个差距也设置为2度。

n = 3
degree_per_unit = (360 - n*2)/max_width

现在legure_per_unit可以在多个图之间共享。每次我们计算last_gap

for(t in sort(unique(Time))) {
    l = d0$Time == t
    
    d0_current = d0[l, c("Year", "Dest_year", "Area_km2")]

    last_gap = 360 - (n-1)*2 - sum(d0_current$Area_km2)*degree_per_unit

    circos.par(gap.after = c(rep(2, n-1), last_gap))
    chordDiagram(d0_current, grid.col = c("2050" = "red", "2100" = "blue", "2200" = "green"))
    circos.clear()

    title(paste0("Time = ", t, ", Sum = ", sum(d0_current$Area_km2)))

    Sys.sleep(1)
}

”在此处输入图像说明”

I will start with your d0 object. I first construct the d0 object but I do not convert everything to characters, just put them as the original numeric format. Also I reorder d0 by Time and Year:

Time = c(10,10,10,100,100,100,200,200,200,5,5,5,50,50,50,0,0,0)
Year = c(2050,2100,2200,2050,2100,2200,2050,2100,2200,2050,2100,2200,2050,2100,2200,2050,2100,2200)
Area_km2 = c(4.3075211,7.1672926,17.2780622,5.9099250,8.2909189,16.9748961,6.5400554,8.9036313,16.5627228,3.0765610,6.3929883,18.0708108,5.3520782,8.4503856,16.7938196,0.5565978,1.8415855,12.5089476)

d0 = data.frame(Time = Time,
    Year = Year,
    Area_km2 = Area_km2,
    Dest_year = Year)

d0 = d0[order(d0$Time, d0$Year), ]

The key thing is to calculate proper values for "gaps" between sectors so that the same unit from data corresponds to the same degree in different plots.

We first calculate the maximal total width of the circular plot:

width = tapply(d0$Area_km2, d0$Time, sum)
max_width = max(width)

We assume there are n sectors (where n = 3 in d0). We let the first n-1 gaps to be 2 degrees and we dynamically adjust the last gap according to the total amount of values in each plot. For the plot with the largest total value, the last gap is also set to 2 degrees.

n = 3
degree_per_unit = (360 - n*2)/max_width

Now degree_per_unit can be shared between multiple plots. Every time we calculate the value for last_gap:

for(t in sort(unique(Time))) {
    l = d0$Time == t
    
    d0_current = d0[l, c("Year", "Dest_year", "Area_km2")]

    last_gap = 360 - (n-1)*2 - sum(d0_current$Area_km2)*degree_per_unit

    circos.par(gap.after = c(rep(2, n-1), last_gap))
    chordDiagram(d0_current, grid.col = c("2050" = "red", "2100" = "blue", "2200" = "green"))
    circos.clear()

    title(paste0("Time = ", t, ", Sum = ", sum(d0_current$Area_km2)))

    Sys.sleep(1)
}

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文