R语言 如何将一组列与另一组列进行比较?

lxkprmvk  于 2023-09-27  发布在  其他
关注(0)|答案(5)|浏览(129)

考虑到我们有以下数据

df <- data.frame(jan=c(10,7,1),
           feb=c(20,9,2),
           mar=c(30,6,3),
           apr=c(5,5,4),
           may=c(0,9,5),
           jun=c(10,8,6)
)

  jan feb mar apr may jun
1  10  20  30   5   0  10
2   7   9   6   5   9   8
3   1   2   3   4   5   6

我期望得到一个新的标志变量,它是通过比较1列集(jan feb mar)与(apr may jun)而得到的,如果1集的值与另一集的值匹配,则flag ='Y',否则'N'。
这里,jan值匹配第一行的jun,因此标志为Y。

jan feb mar apr may jun flag
1  10  20  30   5   0  10    Y
2   7   9   6   5   9   8    Y
3   1   2   3   4   5   6    N
jm2pwxwz

jm2pwxwz1#

如果性能是一个问题,则采用矢量化方法:

set1 <- c("jan", "feb", "mar")
set2 <- c("apr", "may", "jun")

iY <- unique(
  data.table(
    r = rep(1:nrow(df), each = ncol(df)),
    val = c(unlist(t(df[c(set1, set2)]))),
    set = rep(1:2, c(length(set1), length(set2))),
    key = c("r", "val")
  )[r == shift(r) & val == shift(val) & set != shift(set), r]
)
df$flag <- "N"
df$flag[iY] <- "Y"
df
#>   jan feb mar apr may jun flag
#> 1  10  20  30   5   0  10    Y
#> 2   7   9   6   5   9   8    Y
#> 3   1   2   3   4   5   6    N

在一个更大的数据集上与其他几个解决方案进行基准测试。首先将解决方案作为函数实现。

df <- setNames(
  as.data.frame(matrix(sample(10, 6e4, 1), 1e4, 6)),
  c("jan", "feb", "mar", "apr", "may", "jun")
)

f1 <- function(df, set1, set2) {
  df$flag <- sapply(1:nrow(df), \(x) 
                    ifelse(any(unlist(df[x, set1]) %in% unlist(df[x, set2])), "Y", "N"))
  df
}

f2 <- function(df, set1, set2) {
  iY <- unique(
    data.table(
      r = rep(1:nrow(df), each = ncol(df)),
      val = c(unlist(t(df[c(set1, set2)]))),
      set = rep(1:2, c(length(set1), length(set2))),
      key = c("r", "val")
    )[r == shift(r) & val == shift(val) & set != shift(set), r]
  )
  df$flag <- "N"
  df$flag[iY] <- "Y"
  df
}

f3 <- function(df, set1, set2) {
  df$flag <- c("N", "Y")[(lengths(
    Map(
      intersect,
      asplit(df[set1], 1),
      asplit(df[set2], 1)
    )
  ) > 0) + 1]
  df
}

基准

microbenchmark::microbenchmark(
  f1 = f1(df, set1, set2),
  f2 = f2(df, set1, set2),
  f3 = f3(df, set1, set2),
  check = "equal"
)
#> Unit: milliseconds
#>  expr      min        lq       mean   median        uq       max neval
#>    f1 733.1518 778.70590 927.068694 848.8564 999.61955 1687.4810   100
#>    f2   4.9241   5.64215   6.283577   6.3074   6.82695    9.2447   100
#>    f3 119.9982 134.07060 165.306384 151.6871 189.23330  282.1914   100
vof42yt1

vof42yt12#

首先,我们在每次迭代中遍历dataframe的行和unlist dataframe到每个“集合”的向量。然后使用%in%运算符查看第一个集合中的any元素是否出现在第二个集合中。最后将结果分配给dfflag列。
ifelse部分仅用于将逻辑输出转换为“Y”和“N”。如果您对逻辑输出没有问题,则可以忽略它。

df$flag <- sapply(1:nrow(df), \(x) 
                  ifelse(any(unlist(df[x, 1:3]) %in% unlist(df[x, 4:6])), "Y", "N"))
df
#>   jan feb mar apr may jun flag
#> 1  10  20  30   5   0  10    Y
#> 2   7   9   6   5   9   8    Y
#> 3   1   2   3   4   5   6    N
vatpfxk5

vatpfxk53#

你可以尝试

df$flag <- c("N", "Y")[(lengths(
    Map(
        intersect,
        asplit(df[1:3], 1),
        asplit(df[-(1:3)], 1)
    )
) > 0) + 1]

这应该给予

> df
  jan feb mar apr may jun flag
1  10  20  30   5   0  10    Y
2   7   9   6   5   9   8    Y
3   1   2   3   4   5   6    N
axr492tv

axr492tv4#

1)dplyr在每一行上,如果前3列中的任一列在接下来3列中的任一列中,则将flag设置为Y,否则设置为N。

library(dplyr)

df %>%
  rowwise %>%
  mutate(flag = if (any(pick(1:3) %in% pick(4:6))) "Y" else "N") %>%
  ungroup

## # A tibble: 3 × 7
##     jan   feb   mar   apr   may   jun flag 
##   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
## 1    10    20    30     5     0    10 Y    
## 2     7     9     6     5     9     8 Y    
## 3     1     2     3     4     5     6 N

2)碱R

df |>
  by(1:nrow(df), transform, flag =
    if (any(c(jan, feb, mar) %in% c(apr, may, jun))) "Y" else "N") |>
  do.call(what = "rbind")

##   jan feb mar apr may jun flag
## 1  10  20  30   5   0  10    Y
## 2   7   9   6   5   9   8    Y
## 3   1   2   3   4   5   6    N

3)崩溃

library(collapse)

yn <- function(x) if (any(x[1:3] %in% x[4:6])) "Y" else "N"
tfm(df, flag = dapply(df, yn, MARGIN = 1))

##   jan feb mar apr may jun flag
## 1  10  20  30   5   0  10    Y
## 2   7   9   6   5   9   8    Y
## 3   1   2   3   4   5   6    N

4)Base R - 2这使用了(3)中的yn函数。

transform(df, flag = apply(df, 1, yn))

##   jan feb mar apr may jun flag
## 1  10  20  30   5   0  10    Y
## 2   7   9   6   5   9   8    Y
## 3   1   2   3   4   5   6    N

注意事项

问题中的输入df

df <- data.frame(
  jan = c(10, 7, 1),
  feb = c(20, 9, 2),
  mar = c(30, 6, 3),
  apr = c(5, 5, 4),
  may = c(0, 9, 5),
  jun = c(10, 8, 6)
)
gmxoilav

gmxoilav5#

看起来像是您试图标记{jan,feb,mar}中的任何一个与{apr,may,jun}中的任何一个值匹配的行。使用data.table包,可以选择与搜索模式匹配的行,然后用“Y”分配一个新的标志列

library(data.table)
DT<-data.table(df)

DT[jan %in% c(apr, may, jun) | feb %in% c(apr, may, jun) | mar %in% c(apr, may, jun), flag:="Y"]
DT[is.na(flag), flag:="N"]      # if you want N for ones that don't fit your pattern

   jan feb mar apr may jun flag
1:  10  20  30   5   0  10    Y
2:   7   9   6   5   9   8    Y
3:   1   2   3   4   5   6    N

这种方法允许您通过名称指定列,因此,如果列的顺序发生变化,则不太可能中断。

相关问题