动画点直方图,通过观察建立观察(在R中使用gganimate)
Animated dot histogram, built observation by observation (using gganimate in R)
我想从正态分布中采样点,然后使用 gganimate
包一个一个地构建一个点图,直到最后一帧显示完整的点图。
适用于 ~5,000 - 20,000 个点的较大数据集的解决方案是必不可少的。
这是我目前的代码:
library(gganimate)
library(tidyverse)
# Generate 100 normal data points, along an index for each sample
samples <- rnorm(100)
index <- seq(1:length(samples))
# Put data into a data frame
df <- tibble(value=samples, index=index)
df 看起来像这样:
> head(df)
# A tibble: 6 x 2
value index
<dbl> <int>
1 0.0818 1
2 -0.311 2
3 -0.966 3
4 -0.615 4
5 0.388 5
6 -1.66 6
静态图显示正确的点图:
# Create static version
plot <- ggplot(data=df, mapping=aes(x=value))+
geom_dotplot()
然而,gganimate
版本没有(见下文)。它只把点放在 x 轴上,不堆叠它们。
plot+
transition_reveal(along=index)
与此类似的东西是理想的:
信用:https://gist.github.com/thomasp85/88d6e7883883315314f341d2207122a1
试试这个。基本思想是将 obs 分组到帧,即按索引拆分,然后将样本累积到帧,即在第 1 帧中仅显示第一个 obs,在第 2 帧中显示 obs 1 和 2,..... 也许有是一种更优雅的实现方式,但它有效:
library(ggplot2)
library(gganimate)
library(dplyr)
library(purrr)
set.seed(42)
# example data
samples <- rnorm(100)
index <- seq(1:length(samples))
# Put data into a data frame
df <- tibble(value=samples, index=index)
# inflated df. Group obs together into frames
df_ani <- df %>%
split(.$index) %>%
accumulate(~ bind_rows(.x, .y)) %>%
bind_rows(.id = "frame") %>%
mutate(frame = as.integer(frame))
head(df_ani)
#> # A tibble: 6 x 3
#> frame value index
#> <int> <dbl> <int>
#> 1 1 1.37 1
#> 2 2 1.37 1
#> 3 2 -0.565 2
#> 4 3 1.37 1
#> 5 3 -0.565 2
#> 6 3 0.363 3
p_gg <- ggplot(data=df, mapping=aes(x=value))+
geom_dotplot()
p_gg
#> `stat_bindot()` using `bins = 30`. Pick better value with `binwidth`.
p_anim <- ggplot(data=df_ani, mapping=aes(x=value))+
geom_dotplot()
anim <- p_anim +
transition_manual(frame) +
ease_aes("linear") +
enter_fade() +
exit_fade()
anim
#> `stat_bindot()` using `bins = 30`. Pick better value with `binwidth`.
由 reprex package (v0.3.0)
于 2020-04-27 创建
另一种选择是用另一个几何图形绘制点。您需要先对您的数据进行一些计数(然后合并),但这并不需要让您的数据变长。
例如,您可以使用 geom_point
,但挑战在于正确设置点的尺寸,使它们 touch/do 不接触。这取决于设备/文件大小。
但您也可以只使用 ggforce::geom_ellipse
来画点:)
geom_point(设备尺寸试错)
library(tidyverse)
library(gganimate)
set.seed(42)
samples <- rnorm(100)
index <- seq(1:length(samples))
df <- tibble(value = samples, index = index)
bin_width <- 0.25
count_data <- # some minor data transformation
df %>%
mutate(x = plyr::round_any(value, bin_width)) %>%
group_by(x) %>%
mutate(y = seq_along(x))
plot <-
ggplot(count_data, aes(group = index, x, y)) + # group by index is important
geom_point(size = 5)
p_anim <-
plot +
transition_reveal(index)
animate(p_anim, width = 550, height = 230, res = 96)
geom_ellipse(点大小完全控制)
library(ggforce)
plot2 <-
ggplot(count_data) +
geom_ellipse(aes(group = index, x0 = x, y0 = y, a = bin_width/2, b = 0.5, angle = 0), fill = 'black') +
coord_equal(bin_width) # to make the dots look nice and round
p_anim2 <-
plot2 +
transition_reveal(index)
animate(p_anim2)
update 在你提供给 thomas 的惊人例子的 link 中,你可以看到他使用了类似的方法 - 他使用 geom_circle 而不是geom_ellipse,我选择它是因为可以更好地控制垂直和水平半径。
要获得 "falling drops" 效果,您需要 transition_states
和较长的持续时间以及每秒很多帧。
p_anim2 <-
plot2 +
transition_states(states = index, transition_length = 100, state_length = 1) +
shadow_mark() +
enter_fly(y_loc = 12)
animate(p_anim2, fps = 40, duration = 20)
由 reprex package (v0.3.0)
于 2020-04-29 创建
一些灵感来自:
我认为这里的关键是想象您将如何手动创建此动画,也就是说您将在生成的点图中一次添加一个观察点。考虑到这一点,我在这里使用的方法是创建一个 ggplot
对象,该对象由绘图层 = 观察次数组成,然后通过 transition_layer
.
逐层遍历。
# create the ggplot object
df <- data.frame(id=1:100, y=rnorm(100))
p <- ggplot(df, aes(y))
for (i in df$id) {
p <- p + geom_dotplot(data=df[1:i,])
}
# animation
anim <- p + transition_layers(keep_layers = FALSE) +
labs(title='Number of dots: {frame}')
animate(anim, end_pause = 20, nframes=120, fps=20)
请注意,我设置了 keep_layers=FALSE
以避免重叠。如果你绘制初始 ggplot
对象,你就会明白我的意思,因为第一次观察被绘制了 100 次,第二次被绘制了 99 次......等等
如何缩放更大的数据集?
由于帧数 = 观察数,您需要针对可扩展性进行调整。在这里,只需保持 # frames 不变,这意味着您必须让代码将帧分组为段,我正在通过 seq()
函数执行此操作,指定 length.out=100
。另请注意,在新示例中,数据集包含 n=5000
。为了使点图保持在框架内,您需要使点的大小非常小。我可能把这里的点做得有点太小了,但你明白了。现在 # frames = 观察组数。
df <- data.frame(id=1:5000, y=rnorm(5000))
p <- ggplot(df, aes(y))
for (i in seq(0,length(df$id), length.out=100)) {
p <- p + geom_dotplot(data=df[1:i,], dotsize=0.08)
}
anim <- p + transition_layers(keep_layers=FALSE) +
labs(title='Frame: {frame}')
animate(anim, end_pause=20, nframes=120, fps=20)
我想从正态分布中采样点,然后使用 gganimate
包一个一个地构建一个点图,直到最后一帧显示完整的点图。
适用于 ~5,000 - 20,000 个点的较大数据集的解决方案是必不可少的。
这是我目前的代码:
library(gganimate)
library(tidyverse)
# Generate 100 normal data points, along an index for each sample
samples <- rnorm(100)
index <- seq(1:length(samples))
# Put data into a data frame
df <- tibble(value=samples, index=index)
df 看起来像这样:
> head(df)
# A tibble: 6 x 2
value index
<dbl> <int>
1 0.0818 1
2 -0.311 2
3 -0.966 3
4 -0.615 4
5 0.388 5
6 -1.66 6
静态图显示正确的点图:
# Create static version
plot <- ggplot(data=df, mapping=aes(x=value))+
geom_dotplot()
然而,gganimate
版本没有(见下文)。它只把点放在 x 轴上,不堆叠它们。
plot+
transition_reveal(along=index)
与此类似的东西是理想的:
信用:https://gist.github.com/thomasp85/88d6e7883883315314f341d2207122a1
试试这个。基本思想是将 obs 分组到帧,即按索引拆分,然后将样本累积到帧,即在第 1 帧中仅显示第一个 obs,在第 2 帧中显示 obs 1 和 2,..... 也许有是一种更优雅的实现方式,但它有效:
library(ggplot2)
library(gganimate)
library(dplyr)
library(purrr)
set.seed(42)
# example data
samples <- rnorm(100)
index <- seq(1:length(samples))
# Put data into a data frame
df <- tibble(value=samples, index=index)
# inflated df. Group obs together into frames
df_ani <- df %>%
split(.$index) %>%
accumulate(~ bind_rows(.x, .y)) %>%
bind_rows(.id = "frame") %>%
mutate(frame = as.integer(frame))
head(df_ani)
#> # A tibble: 6 x 3
#> frame value index
#> <int> <dbl> <int>
#> 1 1 1.37 1
#> 2 2 1.37 1
#> 3 2 -0.565 2
#> 4 3 1.37 1
#> 5 3 -0.565 2
#> 6 3 0.363 3
p_gg <- ggplot(data=df, mapping=aes(x=value))+
geom_dotplot()
p_gg
#> `stat_bindot()` using `bins = 30`. Pick better value with `binwidth`.
p_anim <- ggplot(data=df_ani, mapping=aes(x=value))+
geom_dotplot()
anim <- p_anim +
transition_manual(frame) +
ease_aes("linear") +
enter_fade() +
exit_fade()
anim
#> `stat_bindot()` using `bins = 30`. Pick better value with `binwidth`.
由 reprex package (v0.3.0)
于 2020-04-27 创建另一种选择是用另一个几何图形绘制点。您需要先对您的数据进行一些计数(然后合并),但这并不需要让您的数据变长。
例如,您可以使用 geom_point
,但挑战在于正确设置点的尺寸,使它们 touch/do 不接触。这取决于设备/文件大小。
但您也可以只使用 ggforce::geom_ellipse
来画点:)
geom_point(设备尺寸试错)
library(tidyverse)
library(gganimate)
set.seed(42)
samples <- rnorm(100)
index <- seq(1:length(samples))
df <- tibble(value = samples, index = index)
bin_width <- 0.25
count_data <- # some minor data transformation
df %>%
mutate(x = plyr::round_any(value, bin_width)) %>%
group_by(x) %>%
mutate(y = seq_along(x))
plot <-
ggplot(count_data, aes(group = index, x, y)) + # group by index is important
geom_point(size = 5)
p_anim <-
plot +
transition_reveal(index)
animate(p_anim, width = 550, height = 230, res = 96)
geom_ellipse(点大小完全控制)
library(ggforce)
plot2 <-
ggplot(count_data) +
geom_ellipse(aes(group = index, x0 = x, y0 = y, a = bin_width/2, b = 0.5, angle = 0), fill = 'black') +
coord_equal(bin_width) # to make the dots look nice and round
p_anim2 <-
plot2 +
transition_reveal(index)
animate(p_anim2)
update 在你提供给 thomas 的惊人例子的 link 中,你可以看到他使用了类似的方法 - 他使用 geom_circle 而不是geom_ellipse,我选择它是因为可以更好地控制垂直和水平半径。
要获得 "falling drops" 效果,您需要 transition_states
和较长的持续时间以及每秒很多帧。
p_anim2 <-
plot2 +
transition_states(states = index, transition_length = 100, state_length = 1) +
shadow_mark() +
enter_fly(y_loc = 12)
animate(p_anim2, fps = 40, duration = 20)
由 reprex package (v0.3.0)
于 2020-04-29 创建一些灵感来自:
我认为这里的关键是想象您将如何手动创建此动画,也就是说您将在生成的点图中一次添加一个观察点。考虑到这一点,我在这里使用的方法是创建一个 ggplot
对象,该对象由绘图层 = 观察次数组成,然后通过 transition_layer
.
# create the ggplot object
df <- data.frame(id=1:100, y=rnorm(100))
p <- ggplot(df, aes(y))
for (i in df$id) {
p <- p + geom_dotplot(data=df[1:i,])
}
# animation
anim <- p + transition_layers(keep_layers = FALSE) +
labs(title='Number of dots: {frame}')
animate(anim, end_pause = 20, nframes=120, fps=20)
请注意,我设置了 keep_layers=FALSE
以避免重叠。如果你绘制初始 ggplot
对象,你就会明白我的意思,因为第一次观察被绘制了 100 次,第二次被绘制了 99 次......等等
如何缩放更大的数据集?
由于帧数 = 观察数,您需要针对可扩展性进行调整。在这里,只需保持 # frames 不变,这意味着您必须让代码将帧分组为段,我正在通过 seq()
函数执行此操作,指定 length.out=100
。另请注意,在新示例中,数据集包含 n=5000
。为了使点图保持在框架内,您需要使点的大小非常小。我可能把这里的点做得有点太小了,但你明白了。现在 # frames = 观察组数。
df <- data.frame(id=1:5000, y=rnorm(5000))
p <- ggplot(df, aes(y))
for (i in seq(0,length(df$id), length.out=100)) {
p <- p + geom_dotplot(data=df[1:i,], dotsize=0.08)
}
anim <- p + transition_layers(keep_layers=FALSE) +
labs(title='Frame: {frame}')
animate(anim, end_pause=20, nframes=120, fps=20)