Delete/overwrite 行部分匹配
Delete/overwrite rows by partial matching
我需要检查行是否部分重复以及 delete/overwrite 那些 2 列与 3 的不同行匹配的行值存在。一个问题是,“真正的”数据框包含几个列表列,这使得某些操作不可行。最好的情况是,如果可以找到匹配项的任何行都将独立于列号进行检查 - 这意味着只保留具有非 NA 值的列最多的行(在所有包含匹配列值的列中)。
o1 o2 o3
1 1 NA NA
2 2 NA NA
3 3 NA NA
4 4 NA NA
5 6 NA NA
6 7 NA NA
7 5 9 NA # this row has only 2 values which match values from row 11 but the last value is na
8 10 NA NA
9 12 NA NA
10 13 NA NA
11 5 9 14 # this row has values in all 3 columns
12 14 NA NA
13 8 11 15 # so does this row
14 16 NA NA
15 17 NA NA
16 18 NA NA
17 19 NA NA
18 20 NA NA
结果应该是相同的数据框 - 只是没有第 7 行或第 7 行被第 11 行覆盖。
这应该很容易做到,但出于某种原因我没有做到(除非有一个复杂的 for 循环,如果以后添加更多列,则很难概括)。有没有直接的方法来做到这一点?
以上 df 的输入:
structure(list(o1 = c(1L, 2L, 3L, 4L, 6L, 7L, 5L, 10L, 12L, 13L,
5L, 14L, 8L, 16L, 17L, 18L, 19L, 20L), o2 = c(NA, NA, NA, NA,
NA, NA, 9L, NA, NA, NA, 9L, NA, 11L, NA, NA, NA, NA, NA), o3 = c(NA,
NA, NA, NA, NA, NA, NA, NA, NA, NA, 14L, NA, 15L, NA, NA, NA,
NA, NA)), row.names = c(NA, -18L), class = "data.frame")
如果已经有类似的答案,请告诉我。
检测重复的部分解决方案,剩下的就是指定要删除哪些行,运行超时了。我继续“复制”了几行。
df=read.table(text="
o1 o2 o3
1 1 NA NA
2 2 NA NA
3 3 NA NA
4 4 NA NA
5 6 NA NA
6 7 NA NA
7 5 9 NA
8 10 NA NA
9 12 NA NA
10 13 NA NA
11 5 9 14
12 14 NA NA
13 8 11 15
14 16 NA NA
15 7 1 2
16 18 NA NA
17 7 1 3
18 20 NA NA",h=T)
主要技巧是计算距离矩阵并检查哪些行的距离为零,因为 dist 会自动估计成对距离,删除缺失值。
tmp=as.matrix(dist(df))
diag(tmp)=NA
tmp[lower.tri(tmp)]=NA
tod=data.frame(which(tmp==0,arr.ind=T))
导致
row col
X7 7 11
X6 6 15
X6.1 6 17
我想到了使用 dplyr
:
library(dplyr)
df %>%
mutate(rn = row_number(),
count_na = rowSums(across(o1:o3, is.na))) %>%
group_by(o1, o2) %>%
slice_min(count_na) %>%
arrange(rn) %>%
ungroup() %>%
select(o1:o3)
这个returns
# A tibble: 17 x 3
o1 o2 o3
<int> <int> <int>
1 1 NA NA
2 2 NA NA
3 3 NA NA
4 4 NA NA
5 6 NA NA
6 7 NA NA
7 10 NA NA
8 12 NA NA
9 13 NA NA
10 5 9 14
11 14 NA NA
12 8 11 15
13 16 NA NA
14 17 NA NA
15 18 NA NA
16 19 NA NA
17 20 NA NA
此解决方案基于以下想法:
- 对于每一行,我们计算该行中
NA
的数量。
- 我们对
o1
和 o2
进行分组以创建属于同一组的数据。这是一个可能的缺陷:也许只按 o1
分组或进行其他分组可能是更好的方法。这取决于您的数据结构:1, <NA>, <NA>
应该被 1, 2, <NA>
覆盖吗?
- 分组后,我们select行数最少的
NA
行。
- 最后我们做一些清理:删除辅助列,排列数据并取消分组。
这是另一种考虑所有列的方法,应该适用于任意数量的列,无论它们的名称或位置如何
library(dplyr)
mydf <- structure(list(o1 = c(1L, 2L, 3L, 4L, 6L, 7L, 5L, 10L, 12L, 13L,
5L, 14L, 8L, 16L, 17L, 18L, 19L, 20L),
o2 = c(NA, NA, NA, NA,
NA, NA, 9L, NA, NA, NA, 9L, NA, 11L, NA, NA, NA, NA, NA),
o3 = c(NA,
NA, NA, NA, NA, NA, NA, NA, NA, NA, 14L, NA, 15L, NA, NA, NA,
NA, NA)),
row.names = c(NA, -18L),
class = "data.frame")
columns <- names(mydf)
dummy_cols <- paste0(columns, "_dummy")
mydf %>%
# duplicate the dataframe
cbind(mydf %>% `names<-`(dummy_cols)) %>%
# arrange across all columns
arrange(across(columns)) %>%
# fill NAs downwards
tidyr::fill(dummy_cols, .direction = "down") %>%
# create a dummy ID
tidyr::unite(id_dummy, dummy_cols, sep = "") %>%
# group by the id
group_by(id_dummy) %>%
# get the first row of each
filter(row_number()==1) %>%
ungroup() %>%
select(columns)
P.S。还将 1 - NA - NA
替换为 1 - 2 - NA
并将 1 - NA - NA
替换为 1 - NA - 3
我需要检查行是否部分重复以及 delete/overwrite 那些 2 列与 3 的不同行匹配的行值存在。一个问题是,“真正的”数据框包含几个列表列,这使得某些操作不可行。最好的情况是,如果可以找到匹配项的任何行都将独立于列号进行检查 - 这意味着只保留具有非 NA 值的列最多的行(在所有包含匹配列值的列中)。
o1 o2 o3
1 1 NA NA
2 2 NA NA
3 3 NA NA
4 4 NA NA
5 6 NA NA
6 7 NA NA
7 5 9 NA # this row has only 2 values which match values from row 11 but the last value is na
8 10 NA NA
9 12 NA NA
10 13 NA NA
11 5 9 14 # this row has values in all 3 columns
12 14 NA NA
13 8 11 15 # so does this row
14 16 NA NA
15 17 NA NA
16 18 NA NA
17 19 NA NA
18 20 NA NA
结果应该是相同的数据框 - 只是没有第 7 行或第 7 行被第 11 行覆盖。
这应该很容易做到,但出于某种原因我没有做到(除非有一个复杂的 for 循环,如果以后添加更多列,则很难概括)。有没有直接的方法来做到这一点?
以上 df 的输入:
structure(list(o1 = c(1L, 2L, 3L, 4L, 6L, 7L, 5L, 10L, 12L, 13L,
5L, 14L, 8L, 16L, 17L, 18L, 19L, 20L), o2 = c(NA, NA, NA, NA,
NA, NA, 9L, NA, NA, NA, 9L, NA, 11L, NA, NA, NA, NA, NA), o3 = c(NA,
NA, NA, NA, NA, NA, NA, NA, NA, NA, 14L, NA, 15L, NA, NA, NA,
NA, NA)), row.names = c(NA, -18L), class = "data.frame")
如果已经有类似的答案,请告诉我。
检测重复的部分解决方案,剩下的就是指定要删除哪些行,运行超时了。我继续“复制”了几行。
df=read.table(text="
o1 o2 o3
1 1 NA NA
2 2 NA NA
3 3 NA NA
4 4 NA NA
5 6 NA NA
6 7 NA NA
7 5 9 NA
8 10 NA NA
9 12 NA NA
10 13 NA NA
11 5 9 14
12 14 NA NA
13 8 11 15
14 16 NA NA
15 7 1 2
16 18 NA NA
17 7 1 3
18 20 NA NA",h=T)
主要技巧是计算距离矩阵并检查哪些行的距离为零,因为 dist 会自动估计成对距离,删除缺失值。
tmp=as.matrix(dist(df))
diag(tmp)=NA
tmp[lower.tri(tmp)]=NA
tod=data.frame(which(tmp==0,arr.ind=T))
导致
row col
X7 7 11
X6 6 15
X6.1 6 17
我想到了使用 dplyr
:
library(dplyr)
df %>%
mutate(rn = row_number(),
count_na = rowSums(across(o1:o3, is.na))) %>%
group_by(o1, o2) %>%
slice_min(count_na) %>%
arrange(rn) %>%
ungroup() %>%
select(o1:o3)
这个returns
# A tibble: 17 x 3
o1 o2 o3
<int> <int> <int>
1 1 NA NA
2 2 NA NA
3 3 NA NA
4 4 NA NA
5 6 NA NA
6 7 NA NA
7 10 NA NA
8 12 NA NA
9 13 NA NA
10 5 9 14
11 14 NA NA
12 8 11 15
13 16 NA NA
14 17 NA NA
15 18 NA NA
16 19 NA NA
17 20 NA NA
此解决方案基于以下想法:
- 对于每一行,我们计算该行中
NA
的数量。 - 我们对
o1
和o2
进行分组以创建属于同一组的数据。这是一个可能的缺陷:也许只按o1
分组或进行其他分组可能是更好的方法。这取决于您的数据结构:1, <NA>, <NA>
应该被1, 2, <NA>
覆盖吗? - 分组后,我们select行数最少的
NA
行。 - 最后我们做一些清理:删除辅助列,排列数据并取消分组。
这是另一种考虑所有列的方法,应该适用于任意数量的列,无论它们的名称或位置如何
library(dplyr)
mydf <- structure(list(o1 = c(1L, 2L, 3L, 4L, 6L, 7L, 5L, 10L, 12L, 13L,
5L, 14L, 8L, 16L, 17L, 18L, 19L, 20L),
o2 = c(NA, NA, NA, NA,
NA, NA, 9L, NA, NA, NA, 9L, NA, 11L, NA, NA, NA, NA, NA),
o3 = c(NA,
NA, NA, NA, NA, NA, NA, NA, NA, NA, 14L, NA, 15L, NA, NA, NA,
NA, NA)),
row.names = c(NA, -18L),
class = "data.frame")
columns <- names(mydf)
dummy_cols <- paste0(columns, "_dummy")
mydf %>%
# duplicate the dataframe
cbind(mydf %>% `names<-`(dummy_cols)) %>%
# arrange across all columns
arrange(across(columns)) %>%
# fill NAs downwards
tidyr::fill(dummy_cols, .direction = "down") %>%
# create a dummy ID
tidyr::unite(id_dummy, dummy_cols, sep = "") %>%
# group by the id
group_by(id_dummy) %>%
# get the first row of each
filter(row_number()==1) %>%
ungroup() %>%
select(columns)
P.S。还将 1 - NA - NA
替换为 1 - 2 - NA
并将 1 - NA - NA
替换为 1 - NA - 3