根据 R 中的条件创建重复行
Create duplicate rows based on conditions in R
我有一个 data.table 看起来像这样
dt <- data.table(ID=c("A","A","B","B"),Amount1=c(100,200,300,400),
Amount2=c(1500,1500,2400,2400),Dupl=c(1,0,1,0))
ID Amount1 Amount2 Dupl
1: A 100 1500 1
2: A 200 1500 0
3: B 300 2400 1
4: B 400 2400 0
我需要复制 Dupl 列中包含 1 的每一行,并将 Amount1 值替换为该复制行中的 Amount2 值。除此之外,我需要在 Dupl 中为重复的行赋予值 2。这意味着它应该如下所示:
ID Amount1 Amount2 Dupl
1: A 100 1500 1
2: A 1500 1500 2
3: A 200 1500 0
4: B 300 2400 1
5: B 2400 2400 2
6: B 400 2400 0
非常感谢任何帮助!
亲切的问候,
蒂姆
这似乎符合您的要求。大概可以细化一下...
library(splitstackshape)
expandRows(dt, dt$Dupl+1, count.is.col = FALSE)[
Dupl != 0, Dupl := cumsum(Dupl), by = ID][
, Amount1 := ifelse(Dupl > 1, Amount2[-1], Amount1)][]
# ID Amount1 Amount2 Dupl
# 1: A 100 1500 1
# 2: A 1500 1500 2
# 3: A 200 1500 0
# 4: B 300 2400 1
# 5: B 2400 2400 2
# 6: B 400 2400 0
使用dplyr
library("data.table")
library("dplyr")
#data
dt <- data.table(ID = c("A", "A", "B", "B"),
Amount1 = c(100, 200, 300, 400),
Amount2 = c(1500, 1500, 2400, 2400),
Dupl = c(1, 0, 1, 0))
#result
rbind(dt,
dt %>%
filter(Dupl == 1) %>%
mutate(Dupl = 2,
Amount1 = Amount2))
# ID Amount1 Amount2 Dupl
# 1: A 100 1500 1
# 2: A 200 1500 0
# 3: B 300 2400 1
# 4: B 400 2400 0
# 5: A 1500 1500 2
# 6: B 2400 2400 2
你可以试试
rbind(dt,dt[Dupl==1][,c('Amount1', 'Dupl') := list(Amount2, 2)])
您可以 rbind
完成正确转换后的子集数据的副本:
rbind(dt,copy(dt[Dupl==1])[,Amount1:=Amount2][,Dupl:=Dupl+1])
ID Amount1 Amount2 Dupl
1: A 100 1500 1
2: A 200 1500 0
3: B 300 2400 1
4: B 400 2400 0
5: A 1500 1500 2
6: B 2400 2400 2
或者,您可以通过子设置获取重复行,然后使用中间步骤转换重复行。这会将重复的行保留在原始行旁边,如问题中的示例所示:
x <- dt[rep(seq(dt[,Dupl]),times=dt[,Dupl==1]+1)]
x[duplicated(x),c("Amount1","Dupl"):=list(Amount2,Dupl+1)]
x
ID Amount1 Amount2 Dupl
1: A 100 1500 1
2: A 1500 1500 2
3: A 200 1500 0
4: B 300 2400 1
5: B 2400 2400 2
6: B 400 2400 0
使用 dplyr 的 left_join
进行复制工作。也许不优雅,但应该很容易理解。
library(data.table)
library(dplyr)
joiner <- data.frame(Dupl = 1, helper_col= 1:2)
dt <- left_join(dt, joiner) %>%
mutate(Dupl = ifelse(helper_col == 2 & !is.na(helper_col), 2, Dupl)) %>%
select(-helper_col) %>%
mutate(Amount1 = ifelse(Dupl == 2, Amount2, Amount1))
> dt
ID Amount1 Amount2 Dupl
1 A 100 1500 1
2 A 1500 1500 2
3 A 200 1500 0
4 B 300 2400 1
5 B 2400 2400 2
6 B 400 2400 0
这里有偏见,但我认为这个 dplyr 解决方案很优雅,而且它的可扩展性也很好,特别是只要 Dupl
总是 <= 2。本质上,它利用了 tidyr::uncount
,它表示,“基于给定列的值 (x),将每一行重复 x 次,从而延长 df。”一旦我们延长了 df,我们就可以使用 dplyr::mutate_at
来替换单元格,前提是它们的值与它们的滞后值相同。
library(tidyverse)
dt %>%
uncount(Dupl + 1) %>%
mutate_at(vars(Amount1),
~case_when(. == lag(.) ~ Amount2, TRUE ~.)) %>%
mutate_at(vars(Dupl),
~case_when(. == lag(.) ~ 2, TRUE ~.))
# ID Amount1 Amount2 Dupl
# 1: A 100 1500 1
# 2: A 1500 1500 2
# 3: A 200 1500 0
# 4: B 300 2400 1
# 5: B 2400 2400 2
# 6: B 400 2400 0
我有一个 data.table 看起来像这样
dt <- data.table(ID=c("A","A","B","B"),Amount1=c(100,200,300,400),
Amount2=c(1500,1500,2400,2400),Dupl=c(1,0,1,0))
ID Amount1 Amount2 Dupl
1: A 100 1500 1
2: A 200 1500 0
3: B 300 2400 1
4: B 400 2400 0
我需要复制 Dupl 列中包含 1 的每一行,并将 Amount1 值替换为该复制行中的 Amount2 值。除此之外,我需要在 Dupl 中为重复的行赋予值 2。这意味着它应该如下所示:
ID Amount1 Amount2 Dupl
1: A 100 1500 1
2: A 1500 1500 2
3: A 200 1500 0
4: B 300 2400 1
5: B 2400 2400 2
6: B 400 2400 0
非常感谢任何帮助! 亲切的问候,
蒂姆
这似乎符合您的要求。大概可以细化一下...
library(splitstackshape)
expandRows(dt, dt$Dupl+1, count.is.col = FALSE)[
Dupl != 0, Dupl := cumsum(Dupl), by = ID][
, Amount1 := ifelse(Dupl > 1, Amount2[-1], Amount1)][]
# ID Amount1 Amount2 Dupl
# 1: A 100 1500 1
# 2: A 1500 1500 2
# 3: A 200 1500 0
# 4: B 300 2400 1
# 5: B 2400 2400 2
# 6: B 400 2400 0
使用dplyr
library("data.table")
library("dplyr")
#data
dt <- data.table(ID = c("A", "A", "B", "B"),
Amount1 = c(100, 200, 300, 400),
Amount2 = c(1500, 1500, 2400, 2400),
Dupl = c(1, 0, 1, 0))
#result
rbind(dt,
dt %>%
filter(Dupl == 1) %>%
mutate(Dupl = 2,
Amount1 = Amount2))
# ID Amount1 Amount2 Dupl
# 1: A 100 1500 1
# 2: A 200 1500 0
# 3: B 300 2400 1
# 4: B 400 2400 0
# 5: A 1500 1500 2
# 6: B 2400 2400 2
你可以试试
rbind(dt,dt[Dupl==1][,c('Amount1', 'Dupl') := list(Amount2, 2)])
您可以 rbind
完成正确转换后的子集数据的副本:
rbind(dt,copy(dt[Dupl==1])[,Amount1:=Amount2][,Dupl:=Dupl+1])
ID Amount1 Amount2 Dupl
1: A 100 1500 1
2: A 200 1500 0
3: B 300 2400 1
4: B 400 2400 0
5: A 1500 1500 2
6: B 2400 2400 2
或者,您可以通过子设置获取重复行,然后使用中间步骤转换重复行。这会将重复的行保留在原始行旁边,如问题中的示例所示:
x <- dt[rep(seq(dt[,Dupl]),times=dt[,Dupl==1]+1)]
x[duplicated(x),c("Amount1","Dupl"):=list(Amount2,Dupl+1)]
x
ID Amount1 Amount2 Dupl
1: A 100 1500 1
2: A 1500 1500 2
3: A 200 1500 0
4: B 300 2400 1
5: B 2400 2400 2
6: B 400 2400 0
使用 dplyr 的 left_join
进行复制工作。也许不优雅,但应该很容易理解。
library(data.table)
library(dplyr)
joiner <- data.frame(Dupl = 1, helper_col= 1:2)
dt <- left_join(dt, joiner) %>%
mutate(Dupl = ifelse(helper_col == 2 & !is.na(helper_col), 2, Dupl)) %>%
select(-helper_col) %>%
mutate(Amount1 = ifelse(Dupl == 2, Amount2, Amount1))
> dt
ID Amount1 Amount2 Dupl
1 A 100 1500 1
2 A 1500 1500 2
3 A 200 1500 0
4 B 300 2400 1
5 B 2400 2400 2
6 B 400 2400 0
这里有偏见,但我认为这个 dplyr 解决方案很优雅,而且它的可扩展性也很好,特别是只要 Dupl
总是 <= 2。本质上,它利用了 tidyr::uncount
,它表示,“基于给定列的值 (x),将每一行重复 x 次,从而延长 df。”一旦我们延长了 df,我们就可以使用 dplyr::mutate_at
来替换单元格,前提是它们的值与它们的滞后值相同。
library(tidyverse)
dt %>%
uncount(Dupl + 1) %>%
mutate_at(vars(Amount1),
~case_when(. == lag(.) ~ Amount2, TRUE ~.)) %>%
mutate_at(vars(Dupl),
~case_when(. == lag(.) ~ 2, TRUE ~.))
# ID Amount1 Amount2 Dupl
# 1: A 100 1500 1
# 2: A 1500 1500 2
# 3: A 200 1500 0
# 4: B 300 2400 1
# 5: B 2400 2400 2
# 6: B 400 2400 0