如何通过对特定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")
答案 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
的另一个想法。将rollapply
与width = 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))][]