当一列只是相同值的重复时,如何加快合并列的速度?
How to speed up combining columns when one column is just a repetition of the same value?
给定以下数据框:
df <-
data.frame(one_letter = rep("a", 5),
other_letters = letters[2:6])
df
#> one_letter other_letters
#> 1 a b
#> 2 a c
#> 3 a d
#> 4 a e
#> 5 a f
我想将两列合并为一列,得到:
#> all_letters_combined
#> 1 a
#> 2 b
#> 3 c
#> 4 d
#> 5 e
#> 6 f
尽管我可以利用 dplyr
&tidyr
并执行以下操作:
library(dplyr, warn.conflicts = FALSE)
library(tidyr)
# yes, it gets the job done
df %>%
pivot_longer(everything()) %>%
select(value) %>%
unique()
#> # A tibble: 6 x 1
#> value
#> <chr>
#> 1 a
#> 2 b
#> 3 c
#> 4 d
#> 5 e
#> 6 f
尽管如此,我仍在寻找 faster/more 的直接方法。这是因为当我们的 df
是一个包含数据帧的列表列时,速度会成为一个问题。这是一个示例,尽管仍然非常小:
library(nycflights13)
library(babynames)
library(tictoc)
bigger_tib <-
tibble(one_df = rep(list(babynames), 10),
other_dfs = list(starwars, flights, mtcars, trees, women, PlantGrowth, ToothGrowth, co2, Titanic, USArrests))
tic()
bigger_tib %>%
pivot_longer(everything()) %>%
select(value) %>%
unique()
#> # A tibble: 11 x 1
#> value
#> <list>
#> 1 <tibble [1,924,665 x 5]>
#> 2 <tibble [87 x 14]>
#> 3 <tibble [336,776 x 19]>
#> 4 <df [32 x 11]>
#> 5 <df [31 x 3]>
#> 6 <df [15 x 2]>
#> 7 <df [30 x 2]>
#> 8 <df [60 x 3]>
#> 9 <ts [468]>
#> 10 <table [4 x 2 x 2 x 2]>
#> 11 <df [50 x 4]>
toc()
#> 0.97 sec elapsed
我知道这个例子不是很好,因为它没有证明有问题的 运行 时间,但在我的真实数据中,这个过程变得非常慢,我想加快速度。
瓶颈是unique
,应用到
数据框列表。 distinct
会更快。另一方面,如果您在旋转数据帧之前已经知道数据帧是唯一的,则为每个数据帧提供唯一的 id
以保持这种关系将是一种更理想的方法。也就是说,请考虑以下基准。
library(dplyr)
library(tidyr)
f1 <- . %>% pivot_longer(everything()) %>% select(value) %>% unique()
f2 <- . %>% pivot_longer(everything()) %>% select(value) %>% distinct()
f3 <- . %>%
rename(one_df = one_df, other_df = other_dfs) %>%
mutate(one_id = 0L, other_id = row_number()) %>%
pivot_longer(starts_with(c("one", "other")), c(NA, ".value"), names_sep = "_") %>%
distinct(id, .keep_all = TRUE)
microbenchmark::microbenchmark(f1(bigger_tib), f2(bigger_tib), f3(bigger_tib), times = 10L)
输出
> f3(bigger_tib)
# A tibble: 11 x 2
df id
<list> <int>
1 <tibble [1,924,665 x 5]> 0
2 <tibble [87 x 14]> 1
3 <df [50 x 2]> 2
4 <df [32 x 11]> 3
5 <df [31 x 3]> 4
6 <df [15 x 2]> 5
7 <df [30 x 2]> 6
8 <df [60 x 3]> 7
9 <ts [468]> 8
10 <table [4 x 2 x 2 x 2]> 9
11 <df [50 x 4]> 10
基准
Unit: milliseconds
expr min lq mean median uq max neval
f1(bigger_tib) 619.5852 623.8327 638.0796 634.4866 644.9060 687.6760 10
f2(bigger_tib) 230.6140 231.6163 234.4957 234.1330 237.1576 238.6012 10
f3(bigger_tib) 4.0693 5.2220 5.5078 5.2996 5.4089 8.6592 10
关于pivot_longer
行的一个特别说明:这意味着我们将"_"
之后的字符用作names_to
,丢弃"_"
之前的字符。如果在 "_"
.
之后具有相同的字符,则所有值都堆叠在同一列中
如果所述问题是将第一列中的唯一值与第二列合并。如果第一列只是一个重复值而第二列包含所有唯一值,那么一个简单的解决方案是:
data.frame(all_letters_combined=c(df[1,1], df[,2]))
如果您需要从结果列中删除重复项(第 2 列中的重复项或第 1 列在第 2 列中重复)。根据 ekoam 的观察,dplyr::distinct()
比 unique()
快
然后这里有一个选项:
distinct(data.frame(all_letters_combined=c(df[1,1], df[,2])))
当然,如果有更多的列和值的不同可能性,则需要更复杂的解决方案。
给定以下数据框:
df <-
data.frame(one_letter = rep("a", 5),
other_letters = letters[2:6])
df
#> one_letter other_letters
#> 1 a b
#> 2 a c
#> 3 a d
#> 4 a e
#> 5 a f
我想将两列合并为一列,得到:
#> all_letters_combined
#> 1 a
#> 2 b
#> 3 c
#> 4 d
#> 5 e
#> 6 f
尽管我可以利用 dplyr
&tidyr
并执行以下操作:
library(dplyr, warn.conflicts = FALSE)
library(tidyr)
# yes, it gets the job done
df %>%
pivot_longer(everything()) %>%
select(value) %>%
unique()
#> # A tibble: 6 x 1
#> value
#> <chr>
#> 1 a
#> 2 b
#> 3 c
#> 4 d
#> 5 e
#> 6 f
尽管如此,我仍在寻找 faster/more 的直接方法。这是因为当我们的 df
是一个包含数据帧的列表列时,速度会成为一个问题。这是一个示例,尽管仍然非常小:
library(nycflights13)
library(babynames)
library(tictoc)
bigger_tib <-
tibble(one_df = rep(list(babynames), 10),
other_dfs = list(starwars, flights, mtcars, trees, women, PlantGrowth, ToothGrowth, co2, Titanic, USArrests))
tic()
bigger_tib %>%
pivot_longer(everything()) %>%
select(value) %>%
unique()
#> # A tibble: 11 x 1
#> value
#> <list>
#> 1 <tibble [1,924,665 x 5]>
#> 2 <tibble [87 x 14]>
#> 3 <tibble [336,776 x 19]>
#> 4 <df [32 x 11]>
#> 5 <df [31 x 3]>
#> 6 <df [15 x 2]>
#> 7 <df [30 x 2]>
#> 8 <df [60 x 3]>
#> 9 <ts [468]>
#> 10 <table [4 x 2 x 2 x 2]>
#> 11 <df [50 x 4]>
toc()
#> 0.97 sec elapsed
我知道这个例子不是很好,因为它没有证明有问题的 运行 时间,但在我的真实数据中,这个过程变得非常慢,我想加快速度。
瓶颈是unique
,应用到
数据框列表。 distinct
会更快。另一方面,如果您在旋转数据帧之前已经知道数据帧是唯一的,则为每个数据帧提供唯一的 id
以保持这种关系将是一种更理想的方法。也就是说,请考虑以下基准。
library(dplyr)
library(tidyr)
f1 <- . %>% pivot_longer(everything()) %>% select(value) %>% unique()
f2 <- . %>% pivot_longer(everything()) %>% select(value) %>% distinct()
f3 <- . %>%
rename(one_df = one_df, other_df = other_dfs) %>%
mutate(one_id = 0L, other_id = row_number()) %>%
pivot_longer(starts_with(c("one", "other")), c(NA, ".value"), names_sep = "_") %>%
distinct(id, .keep_all = TRUE)
microbenchmark::microbenchmark(f1(bigger_tib), f2(bigger_tib), f3(bigger_tib), times = 10L)
输出
> f3(bigger_tib)
# A tibble: 11 x 2
df id
<list> <int>
1 <tibble [1,924,665 x 5]> 0
2 <tibble [87 x 14]> 1
3 <df [50 x 2]> 2
4 <df [32 x 11]> 3
5 <df [31 x 3]> 4
6 <df [15 x 2]> 5
7 <df [30 x 2]> 6
8 <df [60 x 3]> 7
9 <ts [468]> 8
10 <table [4 x 2 x 2 x 2]> 9
11 <df [50 x 4]> 10
基准
Unit: milliseconds
expr min lq mean median uq max neval
f1(bigger_tib) 619.5852 623.8327 638.0796 634.4866 644.9060 687.6760 10
f2(bigger_tib) 230.6140 231.6163 234.4957 234.1330 237.1576 238.6012 10
f3(bigger_tib) 4.0693 5.2220 5.5078 5.2996 5.4089 8.6592 10
关于pivot_longer
行的一个特别说明:这意味着我们将"_"
之后的字符用作names_to
,丢弃"_"
之前的字符。如果在 "_"
.
如果所述问题是将第一列中的唯一值与第二列合并。如果第一列只是一个重复值而第二列包含所有唯一值,那么一个简单的解决方案是:
data.frame(all_letters_combined=c(df[1,1], df[,2]))
如果您需要从结果列中删除重复项(第 2 列中的重复项或第 1 列在第 2 列中重复)。根据 ekoam 的观察,dplyr::distinct()
比 unique()
快
然后这里有一个选项:
distinct(data.frame(all_letters_combined=c(df[1,1], df[,2])))
当然,如果有更多的列和值的不同可能性,则需要更复杂的解决方案。