每个因子水平内所有列的所有可能行对之间的差异

Differences between all possible pairs of rows for all columns within each level of factor

我知道这是一个常见问题,但我做不到。

我想在分类变量name的每个级别内的数据帧中构建所有可能的行对并且然后对所有非因子变量在 name 的每个级别内对这些行进行差分:第 1 行 - 第 2 行,第 1 行 - 第 3 行,...

set.seed(9) 
df <- data.frame(ID = 1:10, 
                 name=as.factor(rep(LETTERS, each=4)[1:10]), 
                 X1 = sample(1001, 10), 
                 X2 = sample(1001, 10), 
                 bool=sample(c(TRUE, FALSE), 10, replace = TRUE),
                 fruit = as.factor(sample(c("Apple", "Orange", "Kiwi" ), 10, replace = TRUE)))

示例如下所示:

   ID name  X1  X2  bool  fruit
1   1    A 222 118 FALSE  Apple
2   2    A  25   9  TRUE   Kiwi
3   3    A 207 883  TRUE Orange
4   4    A 216 301  TRUE   Kiwi
5   5    B 443 492 FALSE  Apple
6   6    B 134 499 FALSE   Kiwi
7   7    B 389 401  TRUE   Kiwi
8   8    B 368 972  TRUE   Kiwi
9   9    C 665 356 FALSE  Apple
10 10    C 985 488 FALSE   Kiwi

我想要一个 13 行的数据框,如下所示:

   ID  name  X1   X2  bool  fruit
1  1-2    A 197  109    -1  Apple
2  1-3    A  15 -765    -1   Kiwi
…

请注意,因子 fruit 应该保持不变。但这是一个奖励,我首先要更改 X1X2 并保留 name 因素。

我知道我可以使用 combn 功能,但我不知道该怎么做。我更喜欢使用 dplyr 包和 group_by 函数的解决方案。

我已经设法用 dplyr 使用

为连续行创建所有差异
varnotfac <- names(df)[!sapply(df, is.factor )] # remove factorial variable
# but not logical variable

library(dplyr)
diff <- df%>%
  group_by(name) %>%
  mutate_at(varnotfac, funs(. - lead(.))) %>% #      
  na.omit() 

我的样品看起来不一样...

   ID name  X1  X2  bool
1   1    A 222 118 FALSE
2   2    A  25   9  TRUE
3   3    A 207 883  TRUE
4   4    A 216 301  TRUE
5   5    B 443 492 FALSE
6   6    B 134 499 FALSE
7   7    B 389 401  TRUE
8   8    B 368 972  TRUE
9   9    C 665 356 FALSE
10 10    C 985 488 FALSE

使用这个,并寻找 here,我们可以做:

library(dplyr)
library(tidyr)
library(purrr)

df %>% 
  group_by(name) %>% 
  nest() %>% 
  mutate(data = map(data, ~as.data.frame(map(.x, ~as.numeric(dist(.)))))) %>% 
  unnest()
# A tibble: 13 x 5
   name     ID    X1    X2  bool
   <fct> <dbl> <dbl> <dbl> <dbl>
 1 A         1   197   109     1
 2 A         2    15   765     1
 3 A         3     6   183     1
 4 A         1   182   874     0
 5 A         2   191   292     0
 6 A         1     9   582     0
 7 B         1   309     7     0
 8 B         2    54    91     1
 9 B         3    75   480     1
10 B         1   255    98     1
11 B         2   234   473     1
12 B         1    21   571     0
13 C         1   320   132     0

虽然这是未签名的。或者:

df %>% 
  group_by(name) %>% 
  nest() %>% 
  mutate(data = map(data, ~as.data.frame(map(.x, ~combn(., 2, diff))))) %>% 
  unnest()
# A tibble: 13 x 5
   name     ID    X1    X2  bool
   <fct> <int> <int> <int> <int>
 1 A         1  -197  -109     1
 2 A         2   -15   765     1
 3 A         3    -6   183     1
 4 A         1   182   874     0
 5 A         2   191   292     0
 6 A         1     9  -582     0
 7 B         1  -309     7     0
 8 B         2   -54   -91     1
 9 B         3   -75   480     1
10 B         1   255   -98     1
11 B         2   234   473     1
12 B         1   -21   571     0
13 C         1   320   132     0

我无法找到如何使用 filter_if / filter_at 保留所有变量,所以我使用了 select_at。所以来自@Axeman 的回答

set.seed(9)
varnotfac <- names(df)[!sapply(df, is.factor )] # names of non-factorial variables

 diff1<- df %>%
  group_by(name) %>%
  select_at(vars(varnotfac)) %>%
  nest() %>% 
  mutate(data = purrr::map(data, ~as.data.frame(map(.x, ~combn(., 2, base::diff))))) %>% 
  unnest()

或者使用 outer 函数,它比 combn

快多了
set.seed(9)
varnotfac <- names(df)[!sapply(df, is.factor )] # names of non-factorial variables

allpairs <- function(v){
  y <- outer(v,v,'-')
  z <- y[lower.tri(y)]
  return(z)
}

diff2<- df %>%
  group_by(name) %>%
  select_at(vars(varnotfac)) %>%
  nest() %>% 
  mutate(data = purrr::map(data, ~as.data.frame(map(.x, ~allpairs(.))))) %>% 
  unnest()
)

可以检查得到的data.frame和

是一样的
all.equal(diff1,diff2)
[1] TRUE