Select table 中的值以外部 table 为条件

Select values in a table conditional to an external table

我想 select 数据集中每个变量(列)的前 N ​​个值,其中 N 因列和行而异,并在另一个 table 中给出。下面是虹膜数据的示例:

data(iris)
head(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa

## Create a fake external table
ext.tab <- data.table(species=c("setosa","versicolor", "virginica" ),N1=c(1:3),N2=c(3:5),N3=c(5:7),N4=c(7:9))
head(ext.tab)

      species N1 N2 N3 N4
1:     setosa  1  3  5  7
2: versicolor  2  4  6  8
3:  virginica  3  5  7  9

现在对于 Iris setosa,我想获得第 1 列('sepal.length' in iris)的第一个最大值(ext.tab 中的 N1数据),然后是第 2 列 (sepal.width) 的三个最大值 (ext.tab 中的 N2),然后是第 3 列 (petal.length) 的五个最大值 (N3),依此类推。然后移动到 Iris versicolor 并执行相同的操作。

结果可以是 table 或每个物种的列表,其中包含值本身或每个变量(列)的行索引。有什么快速实现方法的想法吗?

这是一个使用自定义函数的 tidyverse 方法。该函数将变量名和组名作为字符标量,将最大值的数量作为数字。函数内部是一个使用 .data pronoundplyr 管道。然后,我将 ext.tab 重塑为长格式并按行应用 get_maximum()

library(tidyverse)

get_maximum <- \(.x, .group, .n_max, .dat) {
  .dat %>% 
    filter(Species == .group) %>% 
    arrange(desc(.data[[.x]])) %>% 
    slice(seq_len(.n_max)) %>% 
    pull(.data[[.x]])
}


dat <- as_tibble(ext.tab) %>%
  pivot_longer(-species) %>%
  mutate(name = recode(
    name,
    N1 = "Sepal.Length",
    N2 = "Sepal.Width",
    N3 = "Petal.Length",
    N4 = "Petal.Width"
  )) %>% 
  rowwise() %>% 
  mutate(max_num = list(
    get_maximum(name, species, value, iris)
  )) %>%
  ungroup()

如果您需要唯一的最大值,可以在自定义函数中添加distinct()

get_maximum_unique <- \(.x, .group, .n_max, .dat) {
  .dat %>% 
    filter(Species == .group) %>% 
    distinct(.data[[.x]]) %>% 
    arrange(desc(.data[[.x]])) %>% 
    slice(seq_len(.n_max)) %>% 
    pull(.data[[.x]])
}

这是一个使用 data.table 的选项。我冒昧地重命名了列名。

cols <- setdiff(names(ext.tab), "Species")
iris[ext.tab, on=.(Species), by=.EACHI, 
    .(.(mapply(function(x, n) -head(sort(-x, partial=n), n), 
        x=mget(cols), n=mget(paste0("i.", cols)), SIMPLIFY=FALSE)))]$V1

数据:

library(data.table)
iris <- as.data.table(iris)
ext.tab <- data.table(Species=c("setosa", "versicolor", "virginica"),
    Sepal.Length=c(1:3),
    Sepal.Width=c(3:5),
    Petal.Length=c(5:7),
    Petal.Width=c(7:9))

输出:

[[1]]
[[1]]$Sepal.Length
[1] 5.8

[[1]]$Sepal.Width
[1] 4.4 4.2 4.1

[[1]]$Petal.Length
[1] 1.9 1.9 1.7 1.7 1.7

[[1]]$Petal.Width
[1] 0.4 0.4 0.6 0.4 0.5 0.4 0.4


[[2]]
[[2]]$Sepal.Length
[1] 7.0 6.9

[[2]]$Sepal.Width
[1] 3.4 3.3 3.2 3.2

[[2]]$Petal.Length
[1] 5.1 4.8 4.9 5.0 4.9 4.8

[[2]]$Petal.Width
[1] 1.7 1.6 1.6 1.8 1.5 1.5 1.6 1.5


[[3]]
[[3]]$Sepal.Length
[1] 7.7 7.9 7.7

[[3]]$Sepal.Width
[1] 3.8 3.8 3.6 3.4 3.4

[[3]]$Petal.Length
[1] 6.4 6.3 6.7 6.9 6.7 6.6 6.1

[[3]]$Petal.Width
[1] 2.5 2.5 2.4 2.5 2.4 2.4 2.3 2.3 2.3

简短说明:

  1. 执行左连接iris[ext.tab, on=.(Species),
  2. by=.EACHI 表示 ext.tab
  3. 的每一行
  4. x=mget(cols) 获取 iris
  5. 中的列
  6. mget(paste0("i.", cols))获取每列需要的值的个数
  7. -head(sort(-x, partial=n), n) 执行部分排序并提取前 n 个值
  8. SIMPLIFY=FALSE.(.( )) 只需要 return 结果作为列表