【问题标题】:Filtering observations using multivariate column conditions使用多变量列条件过滤观察
【发布时间】:2019-10-24 14:44:34
【问题描述】:

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

我有一个reference数据框,它包含四列整数值和一个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

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

如果在 reference 表和 ma​​nual 之间的整行中存在完全匹配,则从参考中提取此(那些)并显示该行,如果没有,则减少数量从右到左匹配列,直到存在匹配但不在少于两个变量(列 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. 如果在 reference 表和 ma​​nual 之间的整行中存在完全匹配,则从参考中提取此(那些)并向我显示该行,如果没有然后从右到左减少匹配列的数量,直到存在匹配但不是在少于两个变量(列 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 列,但我认为这并不重要。

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

【问题讨论】:

  • 你能澄清第二个问题的第二个规则吗?这适用于“只有四个变量匹配”的情况 --- 但看起来您将 +/-1 差异应用于有 2 个匹配项的 G 和 I?您的意思是只有“2 个变量匹配”A 和 B?
  • 是的,我想我搞砸了,我的意思是在那个特定的例子中有 2 个变量匹配,但将来我将有 11 个变量并且希望至少有 4 个匹配。我会更新帖子中的文字。

标签: r filter conditional-statements


【解决方案1】:

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

首先,您可以将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

如果您没有完全匹配,将计算dfvec.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 列中非零(最后一列不匹配)。您可以提取 df.best 中包含 4 的行作为最佳匹配:

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)

【讨论】:

  • 看起来最后一个表达式应该有逗号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)),] }
  • 末尾加逗号
  • 谢谢@Ben,我试着让最后一部分更笼统一些,所以从最大匹配数开始。 n.cols &lt;- max(df.best,na.rm=TRUE) #then create vector vec.alt &lt;- c(rep (0,each=n.cols-1),1) 然后检查匹配,非常感谢!
  • 感谢您添加此内容 - 将编辑答案以包括在内。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-07
相关资源
最近更新 更多