在r中的特定条件下从特定列填充一行

Filling a row from a certain column under certain condition in r

当这些列在该行中包含“-3”时,我想用某些特定列(本例中的 B 和 E)的“-3”填充到最后一行。我想出了一个解决方案,但它在我的原始数据集(2435 x 431 个单元格)中非常慢并且有 15 列来检查值 ==“-3”。

在此示例中,要填充“-3”的行是“B”列中的第 4 行和第 10 行以及“D”列中的第 3 行。请注意,4 和 10 在“E”列中也包含值 ==“-3”,但在迭代“B”列时它们已经被填充

library(tidyverse)

values <- as.character(-3:3)

set.seed(123)

data <- tibble(
  A = sample(values, 10, replace = T),
  B = sample(values, 10, replace = T),
  C = sample(values, 10, replace = T),
  D = sample(values, 10, replace = T),
  E = sample(values, 10, replace = T),
  F = sample(values, 10, replace = T)
)

fill_minus_three <- function(x){
  for (i in 1:length(x)){
    if ((names(x)[i] %in% c("B", "E")) && x[i] == "-3"){
      x[i:length(x)] <- "-3"
      break
    }
  }
  return(x)
}

t(apply(data, 1, fill_minus_three)) %>% 
  as_tibble()

#> # A tibble: 10 x 6
#> A     B     C     D     E     F    
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1  3    0     0    -1     1     1    
#> 2  3    2    -3     0     3    -1   
#> 3 -1    2    -3     2    -3    -3   
#> 4  2   -3    -3    -3    -3    -3   
#> 5 -1   -2    -1    -1    -2    -2   
#> 6 -2   -1    -2     3     3     1    
#> 7 -2    1     3     1    -1     1    
#> 8  2   -1    -2     0     0     0    
#> 9 -1   -1    -3     3     1     3    
#> 10 1   -3    -3    -3    -3    -3  

此外,我想使用 map_* 系列,因为其余脚本遵循 tidyverse 方法(但是,这是可选的)。

如果在当前列之前的指定列中遇到任何 -3,您可以通过转换为长格式、按原始行分组并切换值来解决此问题:

library(tidyverse)

columns_I_care_about <- c("B", "D")
data %>%
  mutate(row = row_number()) %>%
  pivot_longer(-row) %>%
  group_by(row) %>%
  mutate(flag = value == "-3" & name %in% columns_I_care_about,
         value = if_else(cumsum(flag) > 0, "-3", value)) %>% 
  ungroup() %>%
  select(row, name, value) %>%
  pivot_wider(names_from = name, values_from = value)

结果

# A tibble: 10 × 7
     row A     B     C     D     E     F    
   <int> <chr> <chr> <chr> <chr> <chr> <chr>
 1     1 3     0     0     -1    1     1    
 2     2 3     2     -3    0     3     -1   
 3     3 -1    2     -3    2     -3    2    
 4     4 2     -3    -3    -3    -3    -3   
 5     5 -1    -2    -1    -1    -2    -2   
 6     6 -2    -1    -2    3     3     1    
 7     7 -2    1     3     1     -1    1    
 8     8 2     -1    -2    0     0     0    
 9     9 -1    -1    -3    3     1     3    
10    10 1     -3    -3    -3    -3    -3  

Si entenc bé,您正在尝试根据 B 列和 E 列中的值更改多列中的值。

不需要 for 循环或 map/apply 函数,您只需使用 mutate 并将其与 across:

配对
library(dplyr)

data |> 
  mutate(across(C:F, ~ if_else(B == "-3", "-3", .x)),
         F = if_else(E == "-3", "-3", F))

输出

#> # A tibble: 10 × 6
#>    A     B     C     D     E     F    
#>    <chr> <chr> <chr> <chr> <chr> <chr>
#>  1 3     0     0     -1    1     1    
#>  2 3     2     -3    0     3     -1   
#>  3 -1    2     -3    2     -3    -3   
#>  4 2     -3    -3    -3    -3    -3   
#>  5 -1    -2    -1    -1    -2    -2   
#>  6 -2    -1    -2    3     3     1    
#>  7 -2    1     3     1     -1    1    
#>  8 2     -1    -2    0     0     0    
#>  9 -1    -1    -3    3     1     3    
#> 10 1     -3    -3    -3    -3    -3

reprex package (v2.0.1)

创建于 2022-06-02

我想了想,我意识到我不需要遍历所有列,只需要检查值 ==“-3”的列。所以我稍微改变了函数,我工作得更快(整个数据集中 0.3 秒,而之前花了两分钟)。然而,它不是 tidy-friendly。 :/

positions <- which(names(data) %in% c("B", "E"))

fill_minus_three <- function(x, positions){
  for (i in positions){
    if (x[i] == "-3"){
      x[i:length(x)] <- "-3"
      break
    }
  }
  return(x)
}

t(apply(data, 1, function(x) fill_minus_three(x, positions))) %>% 
  as_tibble()