分隔行以制作虚拟行

Separate rows to make dummy rows

考虑这个数据框:

dat <- structure(list(col1 = c(1, 2, 0), col2 = c(0, 3, 2), col3 = c(1, 2, 3)), class = "data.frame", row.names = c(NA, -3L))

  col1 col2 col3
1    1    0    1
2    2    3    2
3    0    2    3

如何虚拟化行?即每当有一行有超过 1 个非 0 值时,将该行分成多行,每行有一个非 0 值。

在这种情况下,这将是:

  col1 col2 col3
1    1    0    0
2    0    0    1
3    2    0    0
4    0    3    0
5    0    0    2
6    0    2    0
7    0    0    3

你可以这样做:

library(tidyverse)

dat |> 
  pivot_longer(everything()) |> 
  mutate(id = 1:n()) |> 
  pivot_wider(values_fill = 0) |> 
  filter(!if_all(-id, ~ . == 0)) |> 
  select(-id)

# A tibble: 7 x 3
   col1  col2  col3
  <dbl> <dbl> <dbl>
1     1     0     0
2     0     0     1
3     2     0     0
4     0     3     0
5     0     0     2
6     0     2     0
7     0     0     3

一个非常丑陋的方式是:

library(tidyverse)

dat %>% 
  apply(1, diag) %>% 
  matrix(nrow = 3) %>% 
  t() %>% 
  as.data.frame() %>% 
  rename_with(~ names(dat), everything()) %>% 
  filter(rowSums(.) != 0)

  col1 col2 col3
1    1    0    0
2    0    0    1
3    2    0    0
4    0    3    0
5    0    0    2
6    0    2    0
7    0    0    3

另一种方式,我这里用的是data.table

library(data.table)

x <- rbindlist(apply(dat, 1, function(x) {
  x <- data.table(diag(x, ncol(dat)))
  x[colSums(x) > 0]
}))

setnames(x, names(dat))

x

#    col1 col2 col3
# 1:    1    0    0
# 2:    0    0    1
# 3:    2    0    0
# 4:    0    3    0
# 5:    0    0    2
# 6:    0    2    0
# 7:    0    0    3