按行删除相邻重复项-[R]

时间:2018-11-10 11:31:10

标签: r duplicates

我有一个数据框,其中每一行代表每个人的交互数据。

This should be Uppercase

每个人可以进行以下一种或多种互动:

actions = read.table('C:/Users/Desktop/actions.csv', header = F, sep = ',', na.strings = '', stringsAsFactors = F)

每个人记录的动作时长可能会有所不同,如下所示:

eat, sleep, walk, jump, hop, wake, run

为使长度相等,我在末尾添加了NA填充:

P1: eat,  sleep, sleep, sleep
P2: wake, walk,  eat,   walk, walk, jump, jump, run, run
P3: wake, eat,   walk,  jump, run,  sleep

现在,我的要求是更新每人条目(行数据),以便没有两个连续的条目重复。维持订单非常重要。我需要的输出是:

P1: eat,  sleep, sleep, sleep, NA,   NA,    NA,   NA,  NA
P2: wake, walk,  eat,   walk,  walk, jump,  jump, run, run
P3: wake, eat,   walk,  jump,  run,  sleep, NA,   NA,  NA

默认情况下,列名称为V1,V2,V3...。Vn其中

P1: eat,  sleep, NA,   NA,   NA,   NA,    NA,   NA,  NA
P2: wake, walk,  eat,  walk, jump, run,   NA,   NA,  NA 
P3: wake, eat,   walk, jump, run,  sleep, NA,   NA,  NA

在上面的示例中,P2具有最大长度;所以n =9。所以上例中的总列来自V1-V9。

的输出
n = maximum length of interactions string 

以下问题:Removing Only Adjacent Duplicates in Data Frame in R与我的有点类似,但是有几个区别。即使通过合并上述问题的代码,我也无法解决我的问题。

任何对此的建议将不胜感激!

3 个答案:

答案 0 :(得分:3)

library(tidyverse)

read.csv(text=gsub(" +", "", "P1, eat,  sleep, sleep, sleep, NA,   NA,    NA,   NA,  NA
P2, wake, walk,  eat,   walk,  walk, jump,  jump, run, run
P3, wake, eat,   walk,  jump,  run,  sleep, NA,   NA,  NA"), 
           header = FALSE, stringsAsFactors = FALSE) %>% 
  setNames(c("person", sprintf("i%s", 1:9))) %>% tbl_df() -> xdf

de_dup <- function(x) {
  # remove consecutive dups and keep order
  interactions <- rle(unlist(x, use.names = FALSE)[-1])$values
  # fill in NAs
  interactions <- c(interactions, rep(NA_character_, length(x[-1])-length(interactions)))
  # return a data frame
  as.data.frame(as.list(setNames(c(x[1], interactions), names(x))), stringsAsFactors=FALSE)
}

rowwise(xdf) %>% 
  do(de_dup(.)) %>% 
  ungroup()
## # A tibble: 3 x 10
##   person i1    i2    i3    i4    i5    i6    i7    i8    i9   
## * <chr>  <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
## 1 P1     eat   sleep NA    NA    NA    NA    NA    NA    NA   
## 2 P2     wake  walk  eat   walk  jump  run   NA    NA    NA   
## 3 P3     wake  eat   walk  jump  run   sleep NA    NA    NA 

请求的博览会

由于dups是跨列的,所以最直接的方法(不一定是最快或最少的内存/ CPU密集型)是逐行重新创建数据帧。

  • rowwise()是一项tidyverse函数,可将数据帧按行分成组
  • 然后,我们使用(do()提取每一行,并将其传递给我们创建的函数,以使代码更具可读性和可更新性(不同于将内嵌括号{}的疯狂与分号混淆)与换行符)。 . ==整个行
  • x中的de_dup()参数将是一个命名列表(请阅读do上的文档)
  • 我们将列表与unlist()转换为向量
  • 然后我们将其传递给rle函数,但不传递给人的第一个元素。这不是完全必要的(人将是唯一的),但它具有注意逻辑,因为您知道您正在与人进行交互。查看rle(c("a", "a", "b", "c", "c", "c", "d))的输出以了解其功能。它代表游程长度编码,专为满足您的需求而构建
  • rle的返回值包含一个values元素,该元素具有不包含NA的重复数据删除元素。
  • 由于^^,我们不得不再次填充NA。有很多方法可以做到这一点。我喜欢这种方式。
  • 然后我们必须返回一个数据框(再次检查do()上的文档),以便我们创建一个命名的字符向量并将其转换为数据框
  • do()的末尾,我们仍然有按行分组的数据帧,因此我们需要对其进行取消分组

答案 1 :(得分:1)

这是使用基数R的一种简单方法。我只创建了一个函数,该函数将用NA替换连续的重复项,并按所需顺序重新排列新行-

# function to check consecutive duplicates
ccd <- function(x) {
  # first value can never be duplicate so initiating to 0
  test <- c(0, sapply(1:(length(x)-1), function(i) anyDuplicated(x[i:(i+1)])))
  x[test > 0] <- NA_character_
  x[order(test)]
}

# Original df from dput
> df
  V1 V2 V3   V4   V5   V6   V7   V8   V9
1  S  C  R    S    C    R    S    C    R
2  C  C  C <NA> <NA> <NA> <NA> <NA> <NA>
3  R  R  R    R    R <NA> <NA> <NA> <NA>

for(r in 1:nrow(df)) {
  df[r, ] <- ccd(as.character(df[r, ]))
}

> df
  V1   V2   V3   V4   V5   V6   V7   V8   V9
1  S    C    R    S    C    R    S    C    R
2  C <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA>
3  R <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA>

对于帖子中的演示示例-

df <- read.csv(
text=gsub(" +", "", "P1, eat,  sleep, sleep, sleep, NA,   NA,    NA,   NA,  NA
P2, wake, walk,  eat,   walk,  walk, jump,  jump, run, run
                         P3, wake, eat,   walk,  jump,  run,  sleep, NA,   NA,  NA"), 
               header = FALSE, stringsAsFactors = FALSE)[, -1]

> df
    V2    V3    V4    V5   V6    V7   V8   V9  V10
1  eat sleep sleep sleep <NA>  <NA> <NA> <NA> <NA>
2 wake  walk   eat  walk walk  jump jump  run  run
3 wake   eat  walk  jump  run sleep <NA> <NA> <NA>

for(r in 1:nrow(df)) {
  df[r, ] <- ccd(as.character(df[r, ]))
}

> df
    V2    V3   V4   V5   V6    V7   V8   V9  V10
1  eat sleep <NA> <NA> <NA>  <NA> <NA> <NA> <NA>
2 wake  walk  eat walk jump   run <NA> <NA> <NA>
3 wake   eat walk jump  run sleep <NA> <NA> <NA>

答案 2 :(得分:1)

dplyrreshape2和基数R的组合。首先,它标识所需的重复项并将其替换为NA。然后,将非NA值向左移动。

as.data.frame(t(apply(df %>%
          gather(var, val, -V1) %>% 
          group_by(V1) %>% 
          mutate(val2 = ifelse(val == lag(val), NA, val),
                 val2 = ifelse(var == "V2", paste(val), val2)) %>% 
          dcast(V1~var, value.var = "val2"), 1, function(x) c(x[!is.na(x)], x[is.na(x)]))))

  V1   V2    V3   V4   V5   V6    V7   V8   V9  V10
1 P1  eat sleep <NA> <NA> <NA>  <NA> <NA> <NA> <NA>
2 P2 wake  walk  eat walk jump   run <NA> <NA> <NA>
3 P3 wake   eat walk jump  run sleep <NA> <NA> <NA>

数据(使用@Shree中的代码):

df <- read.csv(text = gsub(" +", "", "P1, eat,  sleep, sleep, sleep, NA,   NA,    NA,   NA,  NA
            P2, wake, walk,  eat,   walk,  walk, jump,  jump, run, run
            P3, wake, eat,   walk,  jump,  run,  sleep, NA,   NA,  NA"), 
               header = FALSE, stringsAsFactors = FALSE)