如何使用dataframe中的当前行值检查上一行值

时间:2017-09-06 05:57:51

标签: r

如何通过对特定ID进行分组,动态地为所有数据帧列检查具有当前行值的上一行值。

我的数据框:

ID  ITEM1     ITEM2    ITEM3           
  1    A         A        A     
  2    C         B        C       
  1    A         B        C
  1    B         A        C    
  2    NA        B        F      
  3    A         A        D     
  4    R         G        J 
  4    H         T        J

对于Ex:

ID  ITEM1     ITEM2    ITEM3    ITEM1change  ITEM2change   ITEM3change                   
  1    A         A        A       0            0             0          
  1    A         B        C       0            1             1
  1    B         A        C       1            1             0 
  2    C         B        C       0            0             0
  2    NA        B        F       1            0             1
  3    A         A        D       0            0             0
  4    R         G        J       0            0             0
  4    H         T        J       1            1             0

我的最终输出将是:

  Fiels         modifiedcout   unmodifiedcount Total      
  ITEM1change     3               5              8                
  ITEM2change     3               5              8    
  ITEM3change     2               6              8

我的数据:

structure(list(ID = c(1, 2, 1, 1, 2, 3, 4, 4), ITEM1 = structure(c(1L, 
3L, 1L, 2L, NA, 1L, 5L, 4L), .Label = c("A", "B", "C", "H", "R"
), class = "factor"), ITEM2 = structure(c(1L, 2L, 2L, 1L, 2L, 
1L, 3L, 4L), .Label = c("A", "B", "G", "T"), class = "factor"), 
    ITEM3 = structure(c(1L, 2L, 2L, 2L, 4L, 3L, 5L, 5L), .Label = c("A", 
    "C", "D", "F", "J"), class = "factor")), .Names = c("ID", 
"ITEM1", "ITEM2", "ITEM3"), row.names = c(NA, -8L), class = "data.frame")

4 个答案:

答案 0 :(得分:4)

可能的解决方案:

library(dplyr)
library(tidyr)

df %>% 
  gather(item, value, -1) %>% 
  group_by(ID, item) %>% 
  mutate(change = lag(value, default = first(value)) != value,
         change = replace(change, is.na(change), TRUE)) %>% 
  group_by(item) %>% 
  summarise(modified = sum(change, na.rm = TRUE),
            unmodified = sum(!change, na.rm = TRUE)) %>% 
  mutate(total = modified + unmodified)

给出:

# A tibble: 3 x 4
   item modified unmodified total
  <chr>    <int>      <int> <int>
1 ITEM1        3          5     8
2 ITEM2        3          5     8
3 ITEM3        2          6     8

答案 1 :(得分:3)

以下是使用rollapply中的zoo的另一个想法。将rollapplywidth = 2一起使用,我们正在测试x是否与x-1不相等。将其包含在as.integer中会产生1s(TRUE)和0s(FALSE)。然后我们将所有NA替换为1,因为您认为它们被修改,并使用colSums对修改/未修改的元素求和。总数只是原始数据框的行数。

library(zoo)

m1 <- do.call(rbind, lapply(split(df, df$ID), function(i) 
                     sapply(i[-1], function(j) 
                     as.integer(c(FALSE, rollapply(j, 2, function(k) k[1] != k[2]))))))

m1 <- replace(m1, is.na(m1), 1)

#giving

#   ITEM1 ITEM2 ITEM3
#      0     0     0
#      0     1     1
#      1     1     0
#      0     0     0
#      1     0     1
#3     0     0     0
#      0     0     0
#      1     1     0

要获得预期的数据框,

final_df <- data.frame(modified = colSums(m1 == 1), 
                       unmodified = colSums(m1 != 1), 
                       Total = nrow(df), stringsAsFactors = FALSE)

给出,

       modified unmodified Total
ITEM1        3          5     8
ITEM2        3          5     8
ITEM3        2          6     8

答案 2 :(得分:0)

如果dat是您的数据,请尝试:

创建ITEMCHANGE变量

dat["ITEM1Change"] <- c(NA, head(dat["ITEM1"], dim(dat)[1] - 1)[[1]])
dat["ITEM2Change"] <- c(NA, head(dat["ITEM2"], dim(dat)[1] - 1)[[1]])
dat["ITEM3Change"] <- c(NA, head(dat["ITEM3"], dim(dat)[1] - 1)[[1]])

然后比较是否有变化

dat$ITEM1Change <- ifelse(dat$ITEM1Change == dat$ITEM1, 0, 1)
dat$ITEM2Change <- ifelse(dat$ITEM2Change == dat$ITEM2, 0, 1)
dat$ITEM3Change <- ifelse(dat$ITEM3Change == dat$ITEM2, 0, 1)

然后分组并总结

library(dplyr)

dat %>% 
  group_by("ITEM1") %>%
  summarise_at(.funs = sum, .vars = "ITEM1Change") -> ITEM1Change

etc.

这是你需要的吗?

答案 3 :(得分:0)

给定数据有许多相同类型的列。这有力地表明数据更好地以长格式而不是宽格式存储。

Jaap's solution正在使用tidyr / dplyr重塑数据。

但是,我想建议一个data.table解决方案,它不会重塑数据。此外,它避免单独处理NA值。

library(data.table)
# coerce to data.table, loop over columns and determine changes to previous row by ID
tmp <- setDT(DF)[, lapply(.SD, function(x) x == shift(x, fill = x[1])), by = ID]
tmp
   ID ITEM1 ITEM2 ITEM3
1:  1  TRUE  TRUE  TRUE
2:  1  TRUE FALSE FALSE
3:  1 FALSE FALSE  TRUE
4:  2  TRUE  TRUE  TRUE
5:  2    NA  TRUE FALSE
6:  3  TRUE  TRUE  TRUE
7:  4  TRUE  TRUE  TRUE
8:  4 FALSE FALSE  TRUE

现在,我们可以计算未更改的行:

tmp[, lapply(.SD, sum, na.rm = TRUE), .SDcols = -"ID"]
   ITEM1 ITEM2 ITEM3
1:     5     5     6

从这里开始,OP的预期结果可以通过两种不同的方式实现

使用melt()

melt(tmp[, lapply(.SD, sum, na.rm = TRUE), .SDcols = -"ID"]
     , measure.vars = patterns("^ITEM"), 
     variable.name = "item", 
     value.name = "unmodified")[
       , c("modified", "Total") := .(nrow(DF) - unmodified, nrow(DF))][]

或通过转置:

as.data.table(
  t(tmp[, lapply(.SD, sum, na.rm = TRUE), .SDcols = -"ID"])
  , keep.rownames = "item")[, setnames(.SD, "V1", "unmodified")][
    , c("modified", "Total") := .(nrow(DF) - unmodified, nrow(DF))][]

两者都返回相同的结果:

    item unmodified modified Total
1: ITEM1          5        3     8
2: ITEM2          5        3     8
3: ITEM3          6        2     8

为了完整起见,这里也是重塑方法的data.table实现。如上所述,NA是通过计算未修改的行排除任何NA来处理的。

melt(setDT(DF), id.vars = "ID", variable.name = "item")[
  , value == shift(value, fill = value[1L]), by = .(ID, item)][
    , .(unmodified = sum(V1, na.rm = TRUE)), by = item][
      , c("modified", "Total") := .(nrow(DF) - unmodified, nrow(DF))][]
相关问题