如何使用 gganimate 获得完整而非部分的饼图
How to get complete, rather than partial, pie charts using gganimate
我在使用 gganimate
和 ggplot
制作动画饼图时遇到问题。
我想每年都吃正常的馅饼,但我的输出完全不同。
您可以使用 mtcars
查看代码示例:
library(ggplot2)
library(gganimate)
#Some Data
df<-aggregate(mtcars$mpg, list(mtcars$cyl,mtcars$carb), sum)
colnames(df)<-c("X","Y","Z")
bp<- ggplot(df, aes(x="", y=Z, fill=X, frame=Y))+
geom_bar(width = 1, stat = "identity") + coord_polar("y", start=0)
gganimate(pie, "output.gif")
这是输出:
当frame
只有一级时效果很好:
ggplot 代码为 df
中的每一行创建了一个单独的堆叠条形图。使用 coord_polar
,这将变成单个饼图,数据框中的每一行都有一个楔形。然后当你使用 gg_animate
时,每一帧只包含对应于给定级别 Y
的楔形。这就是为什么您每次只能获得完整饼图的一部分。
如果您想要 Y
的每个级别都有一个完整的饼图,那么一个选择是为 Y
的每个级别创建一个单独的饼图,然后将这些饼图组合成 GIF .这是一个示例,其中包含一些(我希望)与您的真实数据相似的虚假数据:
library(animation)
# Fake data
set.seed(40)
df = data.frame(Year = rep(2010:2015, 3),
disease = rep(c("Cardiovascular","Neoplasms","Others"), each=6),
count=c(sapply(c(1,1.5,2), function(i) cumsum(c(1000*i, sample((-200*i):(200*i),5))))))
saveGIF({
for (i in unique(df$Year)) {
p = ggplot(df[df$Year==i,], aes(x="", y=count, fill=disease, frame=Year))+
geom_bar(width = 1, stat = "identity") +
facet_grid(~Year) +
coord_polar("y", start=0)
print(p)
}
}, movie.name="test1.gif")
上面GIF中的馅饼都是一样大的。但是你也可以根据 count
的总和为 Year
的每个级别更改馅饼的大小(代码改编自 this SO answer):
library(dplyr)
df = df %>% group_by(Year) %>%
mutate(cp1 = c(0, head(cumsum(count), -1)),
cp2 = cumsum(count))
saveGIF({
for (i in unique(df$Year)) {
p = ggplot(df %>% filter(Year==i), aes(fill=disease)) +
geom_rect(aes(xmin=0, xmax=max(cp2), ymin=cp1, ymax=cp2)) +
facet_grid(~Year) +
coord_polar("y", start=0) +
scale_x_continuous(limits=c(0,max(df$cp2)))
print(p)
}
}, movie.name="test2.gif")
如果我可以编辑一下,虽然动画很酷(但饼图不酷,所以也许动画一堆饼图只会雪上加霜),数据可能更容易理解旧的静态线图。例如:
ggplot(df, aes(x=Year, y=count, colour=disease)) +
geom_line() + geom_point() +
scale_y_continuous(limits=c(0, max(df$count)))
或者这样:
ggplot(df, aes(x=Year, y=count, colour=disease)) +
geom_line() + geom_point(show.legend=FALSE) +
geom_line(data=df %>% group_by(Year) %>% mutate(count=sum(count)),
aes(x=Year, y=count, colour="All"), lwd=1) +
scale_y_continuous(limits=c(0, df %>% group_by(Year) %>%
summarise(count=sum(count)) %>% max(.$count))) +
scale_colour_manual(values=c("black", hcl(seq(15,275,length=4)[1:3],100,65)))
我在使用 gganimate
和 ggplot
制作动画饼图时遇到问题。
我想每年都吃正常的馅饼,但我的输出完全不同。
您可以使用 mtcars
查看代码示例:
library(ggplot2)
library(gganimate)
#Some Data
df<-aggregate(mtcars$mpg, list(mtcars$cyl,mtcars$carb), sum)
colnames(df)<-c("X","Y","Z")
bp<- ggplot(df, aes(x="", y=Z, fill=X, frame=Y))+
geom_bar(width = 1, stat = "identity") + coord_polar("y", start=0)
gganimate(pie, "output.gif")
这是输出:
当frame
只有一级时效果很好:
ggplot 代码为 df
中的每一行创建了一个单独的堆叠条形图。使用 coord_polar
,这将变成单个饼图,数据框中的每一行都有一个楔形。然后当你使用 gg_animate
时,每一帧只包含对应于给定级别 Y
的楔形。这就是为什么您每次只能获得完整饼图的一部分。
如果您想要 Y
的每个级别都有一个完整的饼图,那么一个选择是为 Y
的每个级别创建一个单独的饼图,然后将这些饼图组合成 GIF .这是一个示例,其中包含一些(我希望)与您的真实数据相似的虚假数据:
library(animation)
# Fake data
set.seed(40)
df = data.frame(Year = rep(2010:2015, 3),
disease = rep(c("Cardiovascular","Neoplasms","Others"), each=6),
count=c(sapply(c(1,1.5,2), function(i) cumsum(c(1000*i, sample((-200*i):(200*i),5))))))
saveGIF({
for (i in unique(df$Year)) {
p = ggplot(df[df$Year==i,], aes(x="", y=count, fill=disease, frame=Year))+
geom_bar(width = 1, stat = "identity") +
facet_grid(~Year) +
coord_polar("y", start=0)
print(p)
}
}, movie.name="test1.gif")
上面GIF中的馅饼都是一样大的。但是你也可以根据 count
的总和为 Year
的每个级别更改馅饼的大小(代码改编自 this SO answer):
library(dplyr)
df = df %>% group_by(Year) %>%
mutate(cp1 = c(0, head(cumsum(count), -1)),
cp2 = cumsum(count))
saveGIF({
for (i in unique(df$Year)) {
p = ggplot(df %>% filter(Year==i), aes(fill=disease)) +
geom_rect(aes(xmin=0, xmax=max(cp2), ymin=cp1, ymax=cp2)) +
facet_grid(~Year) +
coord_polar("y", start=0) +
scale_x_continuous(limits=c(0,max(df$cp2)))
print(p)
}
}, movie.name="test2.gif")
如果我可以编辑一下,虽然动画很酷(但饼图不酷,所以也许动画一堆饼图只会雪上加霜),数据可能更容易理解旧的静态线图。例如:
ggplot(df, aes(x=Year, y=count, colour=disease)) +
geom_line() + geom_point() +
scale_y_continuous(limits=c(0, max(df$count)))
或者这样:
ggplot(df, aes(x=Year, y=count, colour=disease)) +
geom_line() + geom_point(show.legend=FALSE) +
geom_line(data=df %>% group_by(Year) %>% mutate(count=sum(count)),
aes(x=Year, y=count, colour="All"), lwd=1) +
scale_y_continuous(limits=c(0, df %>% group_by(Year) %>%
summarise(count=sum(count)) %>% max(.$count))) +
scale_colour_manual(values=c("black", hcl(seq(15,275,length=4)[1:3],100,65)))