如何按多列的值重复行并重新划分
How to repeat rows by their value by multiple columns and divide back
假设我有这个数据框:
> df <- data.frame(A=1:5, B=c(0, 0, 3, 0, 0), C=c(1, 0, 0, 1, 0), D=c(0, 2, 0, 0, 1))
> df
A B C D
1 1 0 1 0
2 2 0 0 2
3 3 3 0 0
4 4 0 1 0
5 5 0 0 1
我如何将其转换为:
A B C D
1 1 0 1 0
2 2 0 0 1
3 2 0 0 1
4 3 1 0 0
5 3 1 0 0
6 3 1 0 0
7 4 0 1 0
8 5 0 0 1
如您所见,有值 2
和 3
,我想按该长度重复它们并将值改回 1。我该怎么做?
如您所见,我还想复制 A
列。
我试过了:
replace(df[rep(rownames(df), select(df, -A)),], 2, 1)
但是它给我一个错误。
一个选项是使用 pmax
从 B
、C
和 D
列中获取最大值,使用 uncount
重复这些行。使用pmin
将大于1的值替换为1.
library(dplyr)
library(tidyr)
df %>%
mutate(repeat_row = pmax(B, C, D)) %>%
uncount(repeat_row) %>%
mutate(across(-A, pmin, 1))
# A B C D
#1 1 0 1 0
#2 2 0 0 1
#3 2 0 0 1
#4 3 1 0 0
#5 3 1 0 0
#6 3 1 0 0
#7 4 0 1 0
#8 5 0 0 1
只是稍微修改一下 Ronak Shah 的回答,我意识到你可以只用 dplyr
:
library(dplyr)
df[rep(rownames(df), apply(select(df, -A), 1, max)),] %>%
as.data.frame(row.names=1:nrow(.)) %>%
mutate(across(-A, pmin, 1))
输出:
A B C D
1 1 0 1 0
2 2 0 0 1
3 2 0 0 1
4 3 1 0 0
5 3 1 0 0
6 3 1 0 0
7 4 0 1 0
8 5 0 0 1
或 rowSums
:
library(dplyr)
df[rep(rownames(df), rowSums(select(df, -A)),] %>%
as.data.frame(row.names=1:nrow(.)) %>%
mutate(across(-A, pmin, 1))
显然,B 到 D 列中只有一个值 > 0,因此我们可以利用部分 rowSums
对使用 > 0
二值化的 B 到 D 列进行 replicate
调用.为了我们可以在 Map
中使用它,我们 t
ranspose 两次。休息是化妆品。
t(do.call(cbind, Map(replicate,
rowSums(df[-1]),
as.data.frame(t(cbind(df[1], df[-1] > 0)))))) |>
as.data.frame() |>
setNames(names(df))
# A B C D
# 1 1 0 1 0
# 2 2 0 0 1
# 3 2 0 0 1
# 4 3 1 0 0
# 5 3 1 0 0
# 6 3 1 0 0
# 7 4 0 1 0
# 8 5 0 0 1
注: R>=4.1 used.
假设我有这个数据框:
> df <- data.frame(A=1:5, B=c(0, 0, 3, 0, 0), C=c(1, 0, 0, 1, 0), D=c(0, 2, 0, 0, 1))
> df
A B C D
1 1 0 1 0
2 2 0 0 2
3 3 3 0 0
4 4 0 1 0
5 5 0 0 1
我如何将其转换为:
A B C D
1 1 0 1 0
2 2 0 0 1
3 2 0 0 1
4 3 1 0 0
5 3 1 0 0
6 3 1 0 0
7 4 0 1 0
8 5 0 0 1
如您所见,有值 2
和 3
,我想按该长度重复它们并将值改回 1。我该怎么做?
如您所见,我还想复制 A
列。
我试过了:
replace(df[rep(rownames(df), select(df, -A)),], 2, 1)
但是它给我一个错误。
一个选项是使用 pmax
从 B
、C
和 D
列中获取最大值,使用 uncount
重复这些行。使用pmin
将大于1的值替换为1.
library(dplyr)
library(tidyr)
df %>%
mutate(repeat_row = pmax(B, C, D)) %>%
uncount(repeat_row) %>%
mutate(across(-A, pmin, 1))
# A B C D
#1 1 0 1 0
#2 2 0 0 1
#3 2 0 0 1
#4 3 1 0 0
#5 3 1 0 0
#6 3 1 0 0
#7 4 0 1 0
#8 5 0 0 1
只是稍微修改一下 Ronak Shah 的回答,我意识到你可以只用 dplyr
:
library(dplyr)
df[rep(rownames(df), apply(select(df, -A), 1, max)),] %>%
as.data.frame(row.names=1:nrow(.)) %>%
mutate(across(-A, pmin, 1))
输出:
A B C D
1 1 0 1 0
2 2 0 0 1
3 2 0 0 1
4 3 1 0 0
5 3 1 0 0
6 3 1 0 0
7 4 0 1 0
8 5 0 0 1
或 rowSums
:
library(dplyr)
df[rep(rownames(df), rowSums(select(df, -A)),] %>%
as.data.frame(row.names=1:nrow(.)) %>%
mutate(across(-A, pmin, 1))
显然,B 到 D 列中只有一个值 > 0,因此我们可以利用部分 rowSums
对使用 > 0
二值化的 B 到 D 列进行 replicate
调用.为了我们可以在 Map
中使用它,我们 t
ranspose 两次。休息是化妆品。
t(do.call(cbind, Map(replicate,
rowSums(df[-1]),
as.data.frame(t(cbind(df[1], df[-1] > 0)))))) |>
as.data.frame() |>
setNames(names(df))
# A B C D
# 1 1 0 1 0
# 2 2 0 0 1
# 3 2 0 0 1
# 4 3 1 0 0
# 5 3 1 0 0
# 6 3 1 0 0
# 7 4 0 1 0
# 8 5 0 0 1
注: R>=4.1 used.