嘿嘿,
我想将一个 for 循环转换为 tidyverse 工作流程,因为我通常遵循 tidyverse 方法并且喜欢使用管道工作流程。我可以使用 for 循环轻松实现我想要的内容。
这是一个最小的可重现示例,其中我按每行标识第一次出现 1 的列(列数)。然后,我用它来索引我的矩阵,并在第一个 1 之前的每列中设置 NA。
# set seed
set.seed(10)
# create matrix of 0 and 1
matrix<-matrix(rbinom(100, 1, 0.4),nrow=10,ncol=10)
# get first column not being a zero (index)
first <- apply(matrix, 1, function(x) min(which(x !=0)))
# set all column before the indexed column to NA
for (i in 1:nrow(matrix)){
if(first[i] > 1) matrix[i, 1:(first[i]-1)] <- NA
}
matrix
这是用
tidyverse
做同样事情的一种方法:
matrix |>
as.data.frame() |>
rowwise() |>
mutate(first = which.max(c_across(everything())),
across(-first, ~ if_else(as.numeric(str_extract(cur_column(), "\\d+")) < first, NA, .))) |> # this is quite janky. If anyone knows a way of selecting column numbers within an across call, please comment with it
ungroup() |>
select(-first)
虽然这很糟糕。如果有人知道在跨调用中选择列号的方法,请评论! :-)
如果您正在使用
tidyverse
,我建议将矩阵从“宽”格式转换为“长”格式,然后执行您想要的任何操作。 “长”格式被认为是“整齐”。
这里的核心概念是使用
cumsum
返回原始行的累积和。如果为零,则将其替换为 NA
,否则不要更改任何内容。
library(tidyverse)
as.data.frame(matrix) %>%
mutate(rn = row_number()) %>%
pivot_longer(-rn) %>%
mutate(value = ifelse(cumsum(value) == 0, NA, value), .by = rn) %>%
pivot_wider() %>%
select(-rn)
# A tibble: 10 × 10
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
<int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 NA 1 1 0 0 0 0 0 1 0
2 NA NA 1 0 0 1 0 0 0 1
3 NA NA 1 0 0 0 0 1 0 0
4 1 0 0 1 1 0 0 0 1 0
5 NA NA NA NA NA NA 1 0 0 0
6 NA NA 1 1 0 0 1 0 0 1
7 NA NA 1 1 0 0 0 0 1 0
8 NA NA NA 1 0 0 0 1 0 0
9 1 0 1 1 0 0 0 0 0 1
10 NA 1 0 0 1 0 0 0 0 0