2016-09-28 112 views
1

我的df有一个link列(类型列表),指定记录是否在df(即它是否有重复项)内匹配。根据匹配列删除df中的重复行

df <- data.frame(id=1:7,link=I(list(c(2,3),c(1,3),c(1,2),NA,NA,7,6))) 

    id link 
1 1 2, 3 
2 2 1, 3 
3 3 1, 2 
4 4 NA 
5 5 NA 
6 6 7 
7 7 6 

我想子集df只保留第一个匹配的行(即相对于由ID订购),用于与链接的记录这些行。我想:

id link 
1 1 2, 3 
2 4 NA 
3 5 NA 
4 6 7 

我已经尝试了循环存储行的id值从dfto_remove删除。目前情况并不奏效,我觉得我正在反思这一点。

to_remove <- character(0) 
for (n in 1:nrow(df)) { 
    links <- df$link[[n]] 
    if (all(is.na(links))) next # skip if no links available 
    add <- ifelse(links %in% to_remove, NA,links) 
    add <- add[!is.na(add)] 
    if (length(add > 0)) to_remove <- c(to_remove,add) 
} 

我可以以更简单的方式做到这一点,避免循环?

+2

为什么不包含最后一行,即'7 6'? – akrun

+0

'id == 6'行中'link'的值是'7'。这告诉你'6'和'7'实际上是相同的记录,我只想保留匹配记录的第一次出现(即只保留'id == 6'并放弃'id == 7') 。 –

回答

5

使用:

library(data.table) 
DT <- data.table(id = rep(df$id, lengths(df$link)), link = unlist(df$link)) 
DT[DT[, .I[!any(id > link) | is.na(link)], by = id]$V1][, .(link = toString(link)), by = id] 

给出:

id link 
1: 1 2, 3 
2: 4 NA 
3: 5 NA 
4: 6 7 

说明

  • 首先由unlisting列表创建一个新的data.frame/data.table在的单元格中并创建一个新的data.frame/data.table。
  • 然后创建满足条件的索引并选择该子集。
  • 最后,将链接列中的值转换回每个id的列表。

或使用dplyr/tidyr组合:

library(dplyr) 
library(tidyr) 
df %>% 
    unnest(link) %>% 
    group_by(id) %>% 
    filter(!any(id > link) | is.na(link)) %>% 
    summarise(link = toString(link)) 

其给出类似的结果:

# A tibble: 4 × 2 
    id link 
    <int> <chr> 
1  1 2, 3 
2  4 NA 
3  5 NA 
4  6  7 

或者用碱R:

dfn <- data.frame(id = rep(df$id, lengths(df$link)), link = unlist(df$link)) 
dfn <- dfn[!dfn$id %in% unique(dfn$id[which(dfn$id > dfn$link)]),] 
aggregate(link ~ id, dfn, toString, na.action = na.pass) 
+1

这是很棒的东西,对'data.table'解决方案的解释确实有帮助。 –

1

由于删除行x取决于所有行的值link1:x-1矢量化很难。我建议循环一次遍历所有行并保存冗余行的(布尔)索引。你在想,只有你的实现有点摇摇欲坠。

df <- data.frame(id=1:7,link=I(list(c(2,3),c(1,3),c(1,2),NA,NA,7,6))) 

keep <- rep(TRUE, nrow(df)) 
for (i in 1:nrow(df)) { 
    idx <- df$link[[i]] 
    idx_larger_than_me <- idx[idx > i] 
    print(idx_larger_than_me) 
    keep[idx_larger_than_me] <- FALSE 
} 
df2 <- df[keep,] 
+0

谢谢彼得,这正是我打算做的。尽管这是一个功能性的答案,但我没有接受它,因为我希望能避免循环。 –

1

如果

第一匹配的行

您对于意思id命令,那么下面应该工作:

library(dplyr) 
library(tidyr) 
result <- df %>% unnest(link) %>% 
       filter(is.na(link) | link > id & !duplicated(link)) %>% 
       group_by(id) %>% 
       nest(link, .key=link) 
print(result) 
### A tibble: 4 x 3 
##  id    link 
## <int>   <list> 
##1  1 <tibble [2 x 1]> 
##2  4 <tibble [1 x 1]> 
##3  5 <tibble [1 x 1]> 
##4  6 <tibble [1 x 1]> 
print(result$link) 
##[[1]] 
### A tibble: 2 x 1 
## link 
## <dbl> 
##1  2 
##2  3 
## 
##[[2]] 
### A tibble: 1 x 1 
## link 
## <dbl> 
##1 NA 
## 
##[[3]] 
### A tibble: 1 x 1 
## link 
## <dbl> 
##1 NA 
## 
##[[4]] 
### A tibble: 1 x 1 
## link 
## <dbl> 
##1  7 

注:

  1. 首先unnest使link s为单独的行(为同一id
  2. is.na(link)保持有没有链接
  3. link > id将继续行为其linkid更大的所有行。当匹配顺序为id时,这将保持第一个匹配,因为链接的行将大于id。
  4. !duplicated(link)将删除链接中的重复项。
  5. 然后,nest