如何使用 R 中另一个数据帧的起始值和结束值对数据帧中的 windows 进行子集化?
How to subset windows in a dataframe using start- and end-values from another dataframe in R?
我有一个时间序列数据的数据框,df1
,我需要从 R 中提取一些 'windows'。windows 的起点和终点我需要在一个单独的数据框的两列中,df2
。起点和终点的值对应于 windows 所需的行号。
在下面的示例中,我是解决方案的一部分,但目前只提取了第一个 window。我如何修改此示例以提取所有四个 windows?这可能是 purrr 的情况吗?
library(tidyverse)
# dataframe of data to subset
df1 <- tibble(my_values = rnorm(100))
# dataframe of windows (i.e. row number IDs) to extract from data
df2 <-tibble::tribble(
~window_start, ~window_end,
3L, 10L,
21L, 25L,
52L, 63L,
78L, 90L
)
# extracted data
df3 <- df1 %>%
slice(df2$window_start : df2$window_end)
(注意。我知道这里有一个类似的问题 - - 但我的实际数据非常大,我很好奇非基于合并的解决方案是否会更快。)
也许用 purrr::map2
试试这个方法
# dataframe of data to subset
df1 <- tibble(my_values = rnorm(100, mean = 45, sd = 30) %>% abs())
# dataframe of windows (i.e. row number IDs) to extract from data
df2 <-tibble::tribble(
~window_start, ~window_end,
3L, 10L,
21L, 25L,
52L, 63L,
78L, 90L
)
subset_thats_in <- function(mini, maxi){
df1 %>%
filter(between(my_values, mini, maxi))
}
purrr::map2(df2$window_start,
df2$window_end,
subset_thats_in)
[[1]]
# A tibble: 4 × 1
my_values
<dbl>
1 6.47
2 8.69
3 7.73
4 7.35
[[2]]
# A tibble: 12 × 1
my_values
<dbl>
1 24.2
2 22.9
3 22.4
4 24.4
5 22.6
6 21.7
7 23.2
8 21.3
9 23.3
10 21.1
11 23.5
12 22.6
[[3]]
# A tibble: 10 × 1
my_values
<dbl>
1 54.0
2 61.4
3 62.5
4 60.8
5 60.5
6 55.5
7 61.4
8 59.0
9 57.9
10 53.3
[[4]]
# A tibble: 6 × 1
my_values
<dbl>
1 87.8
2 79.1
3 80.5
4 82.7
5 85.2
6 80.6
purrr
对于这种数据转换来说是非常有效的内存。但是,如果要复制数据,长度为 10000 的列表可能仍然很笨重。
x = vector(mode = "list", 10000L)
x = purrr::transpose(df2) |> lapply(function(x) df1[x[1]:x[2],])
as.numeric
on a transposed list gets the range, which can be used to subset df1.
对于较大的集合,尝试矢量化方法可能会有用。下面是基本 R 选项,调整 SIMPLIFY = TRUE
以将其简化为向量,以防您使用单列。
f = Vectorize(\(x, y) df1[seq.int(x, y),], SIMPLIFY = F)
f(df2[[1]], df2[[2]])
我们可以使用map2
library(tidyverse)
map2(df2[[1]], df2[[2]], ~ df1[.x:.y, ])
#> [[1]]
#> # A tibble: 8 × 1
#> my_values
#> <dbl>
#> 1 1.33
#> 2 1.27
#> 3 0.415
#> 4 -1.54
#> 5 -0.929
#> 6 -0.295
#> 7 -0.00577
#> 8 2.40
#>
#> [[2]]
#> # A tibble: 5 × 1
#> my_values
#> <dbl>
#> 1 -0.224
#> 2 0.377
#> 3 0.133
#> 4 0.804
#> 5 -0.0571
#>
#> [[3]]
#> # A tibble: 12 × 1
#> my_values
#> <dbl>
#> 1 -0.377
#> 2 2.44
#> 3 -0.795
#> 4 -0.0549
#> 5 0.250
#> 6 0.618
#> 7 -0.173
#> 8 -2.22
#> 9 -1.26
#> 10 0.359
#> 11 -0.0110
#> 12 -0.941
#>
#> [[4]]
#> # A tibble: 13 × 1
#> my_values
#> <dbl>
#> 1 -0.118
#> 2 -0.912
#> 3 -1.44
#> 4 -0.797
#> 5 1.25
#> 6 0.772
#> 7 -0.220
#> 8 -0.425
#> 9 -0.419
#> 10 0.997
#> 11 -0.276
#> 12 1.26
#> 13 0.647
或者创造性地dplyr。
df2 %>%
rowwise() %>%
transmute(windows = list(c_across(starts_with("window")) %>% {df1[.[[1]]:.[[2]], ]}))
#> # A tibble: 4 × 1
#> # Rowwise:
#> windows
#> <list>
#> 1 <tibble [8 × 1]>
#> 2 <tibble [5 × 1]>
#> 3 <tibble [12 × 1]>
#> 4 <tibble [13 × 1]>
由 reprex package (v2.0.1)
创建于 2022-01-09
数据:
set.seed(0)
# dataframe of data to subset
df1 <- tibble(my_values = rnorm(100))
# dataframe of windows (i.e. row number IDs) to extract from data
df2 <- tibble::tribble(
~window_start, ~window_end,
3L, 10L,
21L, 25L,
52L, 63L,
78L, 90L
)
你可以使用 mapply
:
df1[unlist(mapply(function(x,y) x:y, df2$window_start, df2$window_end)),]
# A tibble: 38 x 1
my_values
<dbl>
1 0.671
2 -0.617
3 -0.354
4 2.76
5 0.382
6 -0.488
7 0.889
8 -1.32
9 0.328
10 0.779
# ... with 28 more rows
一个简单的基础 R 解决方案是使用 sequence
函数生成您需要对数据框进行子集化的所有行索引。 sequence
的第一个参数指定要生成的序列的长度,每个序列从第二个参数中给定的数字开始。这应该是非常有效的,因为该函数唯一做的就是创建一个整数序列。
df1[sequence(df2$window_end - df2$window_start + 1L, df2$window_start), ]
输出
> set.seed(1234L)
> df1 <- tibble(my_values = rnorm(100))
> df1[sequence(df2$window_end - df2$window_start + 1L, df2$window_start), ]
# A tibble: 38 x 1
my_values
<dbl>
1 1.08
2 -2.35
3 0.429
4 0.506
5 -0.575
6 -0.547
7 -0.564
8 -0.890
9 0.134
10 -0.491
# ... with 28 more rows
您还可以通过使用 data.table:::vecseq
获得较小的性能改进。代码与上面非常相似:
df1[data.table:::vecseq(df2$window_start, df2$window_end - df2$window_start + 1L, NULL), ]
延伸阅读:
我有一个时间序列数据的数据框,df1
,我需要从 R 中提取一些 'windows'。windows 的起点和终点我需要在一个单独的数据框的两列中,df2
。起点和终点的值对应于 windows 所需的行号。
在下面的示例中,我是解决方案的一部分,但目前只提取了第一个 window。我如何修改此示例以提取所有四个 windows?这可能是 purrr 的情况吗?
library(tidyverse)
# dataframe of data to subset
df1 <- tibble(my_values = rnorm(100))
# dataframe of windows (i.e. row number IDs) to extract from data
df2 <-tibble::tribble(
~window_start, ~window_end,
3L, 10L,
21L, 25L,
52L, 63L,
78L, 90L
)
# extracted data
df3 <- df1 %>%
slice(df2$window_start : df2$window_end)
(注意。我知道这里有一个类似的问题 -
也许用 purrr::map2
# dataframe of data to subset
df1 <- tibble(my_values = rnorm(100, mean = 45, sd = 30) %>% abs())
# dataframe of windows (i.e. row number IDs) to extract from data
df2 <-tibble::tribble(
~window_start, ~window_end,
3L, 10L,
21L, 25L,
52L, 63L,
78L, 90L
)
subset_thats_in <- function(mini, maxi){
df1 %>%
filter(between(my_values, mini, maxi))
}
purrr::map2(df2$window_start,
df2$window_end,
subset_thats_in)
[[1]]
# A tibble: 4 × 1
my_values
<dbl>
1 6.47
2 8.69
3 7.73
4 7.35
[[2]]
# A tibble: 12 × 1
my_values
<dbl>
1 24.2
2 22.9
3 22.4
4 24.4
5 22.6
6 21.7
7 23.2
8 21.3
9 23.3
10 21.1
11 23.5
12 22.6
[[3]]
# A tibble: 10 × 1
my_values
<dbl>
1 54.0
2 61.4
3 62.5
4 60.8
5 60.5
6 55.5
7 61.4
8 59.0
9 57.9
10 53.3
[[4]]
# A tibble: 6 × 1
my_values
<dbl>
1 87.8
2 79.1
3 80.5
4 82.7
5 85.2
6 80.6
purrr
对于这种数据转换来说是非常有效的内存。但是,如果要复制数据,长度为 10000 的列表可能仍然很笨重。
x = vector(mode = "list", 10000L)
x = purrr::transpose(df2) |> lapply(function(x) df1[x[1]:x[2],])
as.numeric
on a transposed list gets the range, which can be used to subset df1.
对于较大的集合,尝试矢量化方法可能会有用。下面是基本 R 选项,调整 SIMPLIFY = TRUE
以将其简化为向量,以防您使用单列。
f = Vectorize(\(x, y) df1[seq.int(x, y),], SIMPLIFY = F)
f(df2[[1]], df2[[2]])
我们可以使用map2
library(tidyverse)
map2(df2[[1]], df2[[2]], ~ df1[.x:.y, ])
#> [[1]]
#> # A tibble: 8 × 1
#> my_values
#> <dbl>
#> 1 1.33
#> 2 1.27
#> 3 0.415
#> 4 -1.54
#> 5 -0.929
#> 6 -0.295
#> 7 -0.00577
#> 8 2.40
#>
#> [[2]]
#> # A tibble: 5 × 1
#> my_values
#> <dbl>
#> 1 -0.224
#> 2 0.377
#> 3 0.133
#> 4 0.804
#> 5 -0.0571
#>
#> [[3]]
#> # A tibble: 12 × 1
#> my_values
#> <dbl>
#> 1 -0.377
#> 2 2.44
#> 3 -0.795
#> 4 -0.0549
#> 5 0.250
#> 6 0.618
#> 7 -0.173
#> 8 -2.22
#> 9 -1.26
#> 10 0.359
#> 11 -0.0110
#> 12 -0.941
#>
#> [[4]]
#> # A tibble: 13 × 1
#> my_values
#> <dbl>
#> 1 -0.118
#> 2 -0.912
#> 3 -1.44
#> 4 -0.797
#> 5 1.25
#> 6 0.772
#> 7 -0.220
#> 8 -0.425
#> 9 -0.419
#> 10 0.997
#> 11 -0.276
#> 12 1.26
#> 13 0.647
或者创造性地dplyr。
df2 %>%
rowwise() %>%
transmute(windows = list(c_across(starts_with("window")) %>% {df1[.[[1]]:.[[2]], ]}))
#> # A tibble: 4 × 1
#> # Rowwise:
#> windows
#> <list>
#> 1 <tibble [8 × 1]>
#> 2 <tibble [5 × 1]>
#> 3 <tibble [12 × 1]>
#> 4 <tibble [13 × 1]>
由 reprex package (v2.0.1)
创建于 2022-01-09数据:
set.seed(0)
# dataframe of data to subset
df1 <- tibble(my_values = rnorm(100))
# dataframe of windows (i.e. row number IDs) to extract from data
df2 <- tibble::tribble(
~window_start, ~window_end,
3L, 10L,
21L, 25L,
52L, 63L,
78L, 90L
)
你可以使用 mapply
:
df1[unlist(mapply(function(x,y) x:y, df2$window_start, df2$window_end)),]
# A tibble: 38 x 1
my_values
<dbl>
1 0.671
2 -0.617
3 -0.354
4 2.76
5 0.382
6 -0.488
7 0.889
8 -1.32
9 0.328
10 0.779
# ... with 28 more rows
一个简单的基础 R 解决方案是使用 sequence
函数生成您需要对数据框进行子集化的所有行索引。 sequence
的第一个参数指定要生成的序列的长度,每个序列从第二个参数中给定的数字开始。这应该是非常有效的,因为该函数唯一做的就是创建一个整数序列。
df1[sequence(df2$window_end - df2$window_start + 1L, df2$window_start), ]
输出
> set.seed(1234L)
> df1 <- tibble(my_values = rnorm(100))
> df1[sequence(df2$window_end - df2$window_start + 1L, df2$window_start), ]
# A tibble: 38 x 1
my_values
<dbl>
1 1.08
2 -2.35
3 0.429
4 0.506
5 -0.575
6 -0.547
7 -0.564
8 -0.890
9 0.134
10 -0.491
# ... with 28 more rows
您还可以通过使用 data.table:::vecseq
获得较小的性能改进。代码与上面非常相似:
df1[data.table:::vecseq(df2$window_start, df2$window_end - df2$window_start + 1L, NULL), ]
延伸阅读: