如何动态重排DataFrame列:将计算列置于对应前缀列之后
问题
为演示需求,我通过示例代码1创建了简单的DataFrame tmp。实际场景中,DataFrame的列数众多且动态可变,列的数量和计算逻辑均依赖用户输入。示例代码2模拟了用户输入触发的动态列新增情况(新增列后缀可为任意内容)。
运行示例代码1后,DataFrame的列顺序如下:
> names(tmp) [1] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" "Species" "Sepal.calc1" [7] "Petal.calc1" "Sepal.calc2" "Petal.calc2"
我需要实现的列排序规则:
Sepal.calc1必须紧跟最后一个以Sepal.开头的原生列(静态示例中为Sepal.Width),Sepal.calc2紧跟Sepal.calc1Petal.calc1必须紧跟最后一个以Petal.开头的原生列(静态示例中为Petal.Width),Petal.calc2紧跟Petal.calc1
由于无法修改mutate()语句的执行顺序,希望通过select()或其他在所有mutate操作后执行的函数实现动态列重排。静态指定列名的方式无法适配动态场景,因此需要一种通用的解决方案。
示例代码1
tmp <- iris[1:10,] %>% mutate(Sepal.calc1 = Sepal.Length * Sepal.Width, Petal.calc1 = Petal.Length * Petal.Width) %>% mutate(Sepal.calc2 = Sepal.calc1 * Sepal.Length, Petal.calc2 = Petal.calc1 * Petal.Length) %>% select( "Sepal.Length", "Sepal.Width", "Sepal.calc1", "Sepal.calc2", "Petal.Length", "Petal.Width", "Petal.calc1", "Petal.calc2", "Species" ) tmp
示例代码2(模拟动态列新增)
tmp <- iris[1:10,] %>% mutate(Sepal.Width1 = Sepal.Width + 1) %>% mutate(Petal.Width1 = Petal.Width + 1) %>% select("Sepal.Length","Sepal.Width","Sepal.Width1","Petal.Length","Petal.Width","Petal.Width1") %>% mutate(Sepal.calc1 = Sepal.Length * Sepal.Width, Sepal.calc2 = Sepal.calc1 * Sepal.Length ) %>% mutate(Petal.calc1 = Petal.Length * Petal.Width, Petal.calc2 = Petal.calc1 * Petal.Length ) tmp
解决方案
可以结合dplyr的选择函数和正则表达式、字符串排序来实现动态列重排,确保无论新增多少原生列,计算列都能按规则紧跟对应的原生列组:
library(dplyr) library(stringr) # 在所有mutate操作后执行以下select语句 tmp <- tmp %>% select( # 筛选所有以Sepal.开头、但不是calc系列的原生列 matches("^Sepal\\.(?!calc)"), # 按数字顺序取Sepal的计算列(确保calc1在calc2前) str_sort(names(.)[str_detect(names(.), "^Sepal\\.calc")], numeric = TRUE), # 同理处理Petal系列列 matches("^Petal\\.(?!calc)"), str_sort(names(.)[str_detect(names(.), "^Petal\\.calc")], numeric = TRUE), # 将剩余的列(如Species)放在最后 everything() )
代码说明
matches("^Sepal\\.(?!calc)"):用正则表达式精准匹配以Sepal.开头,但后续不是calc的列,能自动包含所有新增的原生Sepal列(如Sepal.Width1、Sepal.Width.KooKoo等)str_sort(..., numeric = TRUE):对计算列进行自然排序,避免出现Sepal.calc10排在Sepal.calc2前面的情况everything():自动捕获所有未在前述步骤中指定的列,放在排序后的最后位置
运行上述代码后,示例代码2的列顺序会变为:Sepal.Length, Sepal.Width, Sepal.Width1, Sepal.calc1, Sepal.calc2, Petal.Length, Petal.Width, Petal.Width1, Petal.calc1, Petal.calc2,完全符合预设规则。
内容的提问来源于stack exchange,提问作者Village.Idyot
相关产品推荐
相关产品推荐

