r - 使用多变量列条件过滤观察
问题描述
我不是很有经验的 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
过滤规则应如下:
如果在参考表和手册之间的整行中存在完全匹配,则从参考中提取此(那些)并向我显示该行,如果没有,则从右到左减少匹配列的数量,直到有匹配但不在小于之间两个变量(A、B 列)。
从我只有两个变量匹配的那些行中过滤那些在右侧的列中有 ± 1 差异的行。所以我应该从上面示例的参考表中过滤案例G和I。
继续我上面的方式,我会做以下事情:
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 列,但我认为这并不重要。
牢记这一目标——您建议如何进行?谢谢!
解决方案
这需要整理很多,但我有一些想法可能会有所帮助。
首先,您可以保留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)
推荐阅读
- javascript - 一些网站如何在opera mini的客户端运行javascript
- javascript - React-navigation - 底部导航
- swift - 如何处理核心数据的并发
- rust - 我可以直接在 HashMap 中使用哈希吗?
- elasticsearch - 如何从 Elasticsearch 响应中读取距离
- windows - 删除 Windows Pipe 上的第一行/字符
- java - rJava .jnew java.lang.NoSuchMethodError
- ios - Fabric crashlytics 在某些设备中不显示崩溃
- highcharts - 如何创建以非数字为值的散点图?
- dart - 如何在渡槽中捕获发布请求?