原始数据:
> dt = data.table(v1 = c(3,1,1,5,6,12,13,11,10,0,2,1,3))
> dt
v1
1: 3
2: 1
3: 1
4: 5
5: 6
6: 12
7: 13
8: 11
9: 10
10: 0
11: 2
12: 1
13: 3
我想根据值将v1
分为3组:
> dt %>% mutate(group = case_when(v1 <5 ~ 1,
+ v1 >=5 & v1 <10 ~ 2,
+ v1 >= 10 ~3))
v1 group
1 3 1
2 1 1
3 1 1
4 5 2
5 6 2
6 12 3
7 13 3
8 11 3
9 10 3
10 0 1
11 2 1
12 1 1
13 3 1
但是我还想添加一条规则,如果一个组中的行总数低于3,则取这些行的平均值,并将其与该组之前和之后的行(v1)进行比较,并且最接近平均值的那个值都会吸收该组。
在上面的示例中,组2仅具有2行,因此我取其平均值(5.5)并与上方(1)和下方(12)的值进行比较。由于较小的值更接近平均值,因此这些行将成为组1,从而使所需的输出如下所示:
v1 group
1 3 1
2 1 1
3 1 1
4 5 1
5 6 1
6 12 3
7 13 3
8 11 3
9 10 3
10 0 1
11 2 1
12 1 1
13 3 1
我做了几次尝试都没有用,非常感谢dplyr
或data.table
解决方案。
答案 0 :(得分:1)
使用check this code pen link
的一个选项可能是创建一个新列,该列将保留一个dplyr
的帐户,并比较那些较少的组上方和下方的一行的row_number
值超过3行,并根据该行分配新的组。 v1
是最终输出。
change
答案 1 :(得分:1)
首先,计算原始分组并汇总:
gDT = dt[, .(.N, m = mean(v1)), by=.(
ct = ct <- cut(v1, c(-Inf, 5, 10, Inf), right=FALSE, labels=FALSE),
g = rleid(ct)
)]
ct g N m
1: 1 1 3 1.666667
2: 2 2 2 5.500000
3: 3 3 4 11.500000
4: 1 4 4 1.500000
标记组以更改m
并将其与上方和下方最接近的不变组进行比较:
gDT[, flag := N < 3]
gDT[, res := ct]
gDT[flag == TRUE, res := {
ffDT = gDT[flag == FALSE]
# nearest eligible rows going up and down -- possibly NA if at top or bottom
w_dn = ffDT[.(g = .SD$g - 1L), on=.(g), roll=TRUE, which=TRUE]
w_up = ffDT[.(g = .SD$g + 1L), on=.(g), roll=-Inf, which=TRUE]
# diffs of the mean against eligible rows up and down
diffs = lapply(list(dn = w_dn, up = w_up), function(w) abs(ffDT$m[w] - m))
# if/else for whichever is nearer, ties broken in favor of up
replace(ffDT$ct[w_dn], diffs$up < diffs$dn, ffDT$ct[w_up])
}]
ct g N m flag res
1: 1 1 3 1.666667 FALSE 1
2: 2 2 2 5.500000 TRUE 1
3: 3 3 4 11.500000 FALSE 3
4: 1 4 4 1.500000 FALSE 1
像这样创建一个单独的表,可以很容易地检查工作(查看标记的组,检查N
和ct
,将m
与最近的未标记邻居进行比较,等等)。 / p>
要添加回原始表,一种方法是:
dt[, res := gDT$res[ rleid(cut(v1, c(-Inf, 5, 10, Inf), right=FALSE, labels=FALSE)) ] ]
v1 ct res
1: 3 1 1
2: 1 1 1
3: 1 1 1
4: 5 2 1
5: 6 2 1
6: 12 3 3
7: 13 3 3
8: 11 3 3
9: 10 3 3
10: 0 1 1
11: 2 1 1
12: 1 1 1
13: 3 1 1
详细信息:上面的步骤比@RonakShah的答案要复杂得多,因为我认为这里的“组”适用于连续的行:
但是我还想添加一条规则,如果一个组中的行总数低于3,则取这些行的平均值,并将其与该组之前和之后的行(v1)进行比较,并且最接近平均值的那个值都会吸收该组。
否则,标准的定义不明确-如果存在一组大小为2的组,但两行不是连续的,则没有“紧接在该组之前和之后”的比较。
答案 2 :(得分:1)
基于Frank的cut
和rleid(ct)
:
#from Frank's answer
dt[,
c("ct", "g") := {
ct <- cut(v1, c(-Inf, 5, 10, Inf), right=FALSE, labels=FALSE)
.(ct, rleid(ct))
}
]
#calculate mean
dt[, c("N", "m") := .(.N, m=mean(v1)), by=.(ct, g)]
#store last/first value from prev/next for rolling join later
ct_dt <- dt[, c(.(ct=ct, g=g), shift(.(v1, g), c(1L, -1L)))][,
.(near_v1=c(V3[1L], V4[.N]), new_ct=c(V5[1L], V6[.N])), .(ct, g)]
#update join for those with less than 3 rows
dt[N<3L, ct := ct_dt[.SD, on=.(ct, g, near_v1=m), roll="nearest", new_ct]]
#delete unwanted columns
dt[, c("g","N","m") := NULL]
输出:
v1 ct
1: 3 1
2: 1 1
3: 1 1
4: 5 1
5: 6 1
6: 12 3
7: 13 3
8: 11 3
9: 10 3
10: 0 1
11: 2 1
12: 1 1
13: 3 1