You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在R语言中实现当move_left==1时将多列非NA值左移?

问题描述

需要将R语言中的tibble数据框df转换为目标数据框df_want,转换规则如下:

  • 当move_left列的值为1时,将该行v1至v5列中的非NA值向左移动,填补左侧的NA位置,右侧补NA;
  • 当move_left列的值为0时,保持原行数据不变。

原始数据与目标数据定义如下:

library(tibble)

df <- tribble(
  ~id, ~v1, ~v2, ~v3, ~v4, ~v5, ~move_left,
   1L, "a",  NA, "b",  NA, "c",          0L,
   2L, "a",  NA, "b",  NA, "c",          1L,
   3L,  NA, "a",  NA, "b",  NA,          0L,
   4L,  NA,  NA, "a",  NA,  NA,          1L,
   5L,  NA,  NA, "a",  NA, "b",          1L,
   6L, "a", "b",  NA, "c", "d",          1L
  )

df_want <- tribble(
  ~id, ~v1, ~v2, ~v3, ~v4, ~v5, ~move_left,
   1L, "a",  NA, "b",  NA, "c",          0L,
   2L, "a", "b", "c",  NA,  NA,          1L,
   3L,  NA, "a",  NA, "b",  NA,          0L,
   4L, "a",  NA,  NA,  NA,  NA,          1L,
   5L, "a", "b",  NA,  NA,  NA,          1L,
   6L, "a", "b", "c", "d",  NA,          1L
  )
解决方案

可以结合dplyr和purrr包实现需求,核心逻辑是逐行处理:如果当前行move_left为1,就提取该行v1-v5的非NA值靠左排列,剩余位置补NA;move_left为0则原样保留。

代码实现

library(dplyr)
library(purrr)
library(stringr)

df_result <- df %>%
  rowwise() %>%
  mutate(
    across(v1:v5, ~{
      if (move_left == 1) {
        # 提取当前行v1-v5的非NA值
        non_na_vals <- c(v1, v2, v3, v4, v5)[!is.na(c(v1, v2, v3, v4, v5))]
        # 生成长度为5的向量:非NA值在前,后面补NA
        filled_vals <- c(non_na_vals, rep(NA, 5 - length(non_na_vals)))
        # 根据当前列的序号取对应位置的值
        filled_vals[str_extract(cur_column(), "\\d") %>% as.integer()]
      } else {
        .x
      }
    })
  ) %>%
  ungroup()

# 验证结果是否匹配目标数据框
all.equal(df_result, df_want)
#> [1] TRUE

代码解释

  1. rowwise():让后续操作按行执行,保证每一行的处理独立;
  2. across(v1:v5, ...):对v1到v5列逐一处理;
  3. 分支逻辑:
    • 当move_left=1时:先筛选出当前行v1-v5的非NA值,再用NA补全为5个元素的向量,最后根据当前列的编号(比如v1对应第1位)提取对应值;
    • 当move_left=0时:直接保留原列的值;
  4. ungroup():取消行分组,恢复数据框的常规结构。

另一种简洁实现(用tidyr)

如果习惯用宽长格式转换的思路,也可以用tidyr的pivot_longer和pivot_wider实现:

library(tidyr)
library(dplyr)

df_result2 <- df %>%
  # 将v1-v5转成长格式
  pivot_longer(cols = v1:v5, names_to = "var", values_to = "val", values_drop_na = FALSE) %>%
  group_by(id) %>%
  mutate(
    # 当move_left=1时,重新分配列名:非NA值按顺序对应v1-v5,剩余位置留空
    new_var = if (move_left == 1) {
      non_na_pos <- which(!is.na(val))
      new_pos <- c(non_na_pos, setdiff(1:5, non_na_pos))
      paste0("v", rank(new_pos, ties.method = "first"))
    } else {
      var
    }
  ) %>%
  # 转回宽格式
  pivot_wider(id_cols = c(id, move_left), names_from = new_var, values_from = val) %>%
  # 保证列顺序和原数据一致
  select(id, v1:v5, move_left)

all.equal(df_result2, df_want)
#> [1] TRUE

内容的提问来源于stack exchange,提问作者bgxq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 04:32:45