首页 > 解决方案 > 使用多变量列条件过滤观察

问题描述

我不是很有经验的 R 用户,所以寻求建议如何优化我所构建的内容以及继续前进的方向。

我有一个参考数据框,它包含四列整数值和一个 ID。

df <- matrix(ncol=5,nrow = 10)
colnames(df) <- c("A","B","C","D","ID")
# df
for (i in 1:10){
        df[i,1:4] <- sample(1:5,4, replace = TRUE)
}
df <- data.frame(df)
df$ID <- make.unique(rep(LETTERS,length.out=10),sep='')
df
 A B C D ID
1  2 4 3 5  A
2  5 1 3 5  B
3  3 3 5 3  C
4  4 3 1 5  D
5  2 1 2 5  E
6  5 4 4 5  F
7  4 4 3 3  G
8  2 1 5 5  H
9  4 4 1 3  I
10 4 2 2 2  J

第二个数据框有手动输入,它是用户输入,我想稍后把它变成闪亮的应用程序,这就是为什么我也要求优化,因为我的代码对我来说似乎不是很整洁。

df.man <- data.frame(matrix(ncol=5,nrow=1))
colnames(df.man) <- c("A","B","C","D","ID")
df.man$ID <- c("man")
df.man$A <- 4
df.man$B <- 4
df.man$C <- 3
df.man$D <- 4
df.man
 A B C D  ID
 4 4 3 4 man

我想按照以下规则从引用中依次过滤行:

如果在参考表和手册之间的整行中存在完全匹配,则从参考中提取此(那些)并向我显示该行,如果没有,则从右到左减少匹配列的数量,直到有匹配但不在小于之间两个变量(A、B 列)。

所以以我有限的知识,我写了这个:

# subtraction manual from reference 
df <- df %>% dplyr::mutate(Adiff=A-df.man$A)%>%
        dplyr::mutate(Bdiff=B-df.man$B)%>% 
        dplyr::mutate(Cdiff=C-df.man$C) %>% 
        dplyr::mutate(Ddiff=D-df.man$D)

# check manually how much in a row has zero difference and filter those
ifelse(nrow(df%>%filter(Adiff==0 & Bdiff==0 & Cdiff==0 & Ddiff==0)) != 0,
       df0<-df%>%filter(Adiff==0 & Bdiff==0 & Cdiff==0 & Ddiff==0),
       ifelse(nrow(df%>%filter(Adiff==0 & Bdiff==0 & Cdiff==0)) != 0,
              df0<-df%>%filter(Adiff==0 & Bdiff==0 & Cdiff==0),
              ifelse(nrow(df%>%filter(Adiff==0 & Bdiff==0)) != 0,
              df0<-df%>%filter(Adiff==0 & Bdiff==0),
              "less then two exact match")
       ))

tbl_df(df0[,1:5]) 

# A tibble: 1 x 5
      A     B     C     D ID   
  <int> <int> <int> <int> <chr>
1     4     4     3     3 G    

它有效并找到了 ID G,但对我来说看起来很丑。所以第一个问题是 - 有什么推荐的方法来改进这个?有没有我缺少的功能、包或东西?

第二个问题-我想使情况复杂化。

假设我们有参考数据集。

A B C D ID
2 4 3 5  A
5 1 3 5  B
3 3 5 3  C
4 3 1 5  D
2 1 2 5  E
5 4 4 5  F
4 4 3 3  G
2 1 5 5  H
4 4 1 3  I
4 2 2 2  J

手动输入是

A B C D ID
4 4 2 2 man

过滤规则应如下:

  1. 如果在参考表和手册之间的整行中存在完全匹配,则从参考中提取此(那些)并向我显示该行,如果没有,则从右到左减少匹配列的数量,直到有匹配但不在小于之间两个变量(A、B 列)。

  2. 从我只有两个变量匹配的那些行中过滤那些在右侧的列中有 ± 1 差异的行。所以我应该从上面示例的参考表中过滤案例GI。

继续我上面的方式,我会做以下事情:

ifelse(nrow(df0%>%filter(Cdiff %in% (-1:1) & Ddiff %in% (-1:1)))>0,
       df01 <- df0%>%filter(Cdiff %in% (-1:1) & Ddiff %in% (-1:1)),
       ifelse(nrow(df0%>%filter(Cdiff %in% (-1:1)))>0,
              df01<- df0%>%filter(Cdiff %in% (-1:1)),
       "NA"))

最后大约有 11 列,但我认为这并不重要。

牢记这一目标——您建议如何进行?谢谢!

标签: rfilterconditional-statements

解决方案


这需要整理很多,但我有一些想法可能会有所帮助。

首先,您可以保留df一个矩阵,并为您的字母使用行名。就像是:

set.seed(2)

df

  A B C D
A 5 1 5 1
B 4 5 1 2
C 3 1 3 2
D 3 1 1 4
E 3 1 5 3
F 1 5 5 2
G 2 3 4 3
H 1 1 5 1
I 2 4 5 5
J 4 2 5 5

为了演示,您可以使用一个向量manual作为输入:

# Complete match example
vec.man <- c(3, 1, 5, 3)

要检查手动输入和参考(所有 4 列)之间的完全匹配,以及所有数字,您可以执行以下操作:

df[apply(df, 1, function(x) all(x == vec.man)), ]

A B C D 
3 1 5 3

如果您没有完全匹配,将计算df和之间的差异vec.man

# Change example vec.man
vec.man <- c(3, 1, 5, 2)

df.diff <- sweep(df, 2, vec.man)

   A B  C  D
A  2 0  0 -1
B  1 4 -4  0
C  0 0 -2  0
D  0 0 -4  2
E  0 0  0  1
F -2 4  0  0
G -1 2 -1  1
H -2 0  0 -1
I -1 3  0  3
J  1 1  0  3

以 0 开头和继续的差异将是您的最佳匹配(与从右到左迭代地查看相同)。然后,您的最佳匹配是每行中第一个非零元素的列:

df.best <- apply(df.diff, 1, function(x) which(x!=0)[1])

A B C D E F G H I J 
1 1 3 3 4 1 1 1 1 1 

您可以看到最佳匹配是E第 4 列中的非零(最后一列不匹配)。您可以提取具有 4 indf.best作为最佳匹配的行:

df.match <- df[which(df.best == max(df.best, na.rm = T)), ]

A B C D 
3 1 5 3 

最后,如果您想要所有最接近匹配 +/- 1 的行(如果只有 2 个匹配),您可以检查最佳匹配的数量(应该是 3)。然后,将差异与向量进行比较,c(0,0,1)这意味着 2 个匹配项,然后第 3 列偏离 +/- 1:

# Example vec.man with only 2 matches
vec.man <- c(3, 1, 6, 9)

> df.match
  A B C D
C 3 1 3 2
D 3 1 1 4
E 3 1 5 3

if (max(df.best, na.rm = T) == 3) {
  vec.alt = c(0, 0, 1)
  df[apply(df.diff[,1:3], 1, function(x) all(abs(x) == vec.alt)), ]
}

A B C D 
3 1 5 3

这应该可扩展为 11 列和 4 个匹配项。

为了概括不同数量的列,@IlyaT 建议:

n.cols <- max(df.best, na.rm=TRUE) 
vec.alt <- c(rep(0, each=n.cols-1), 1)

推荐阅读