关于如何将列移到第一位或最后一位有很好的疑问和答案。
使用dplyr
的最佳答案分别类似于:
iris2 <- iris %>% head(2)
iris2 %>% select( Sepal.Width, everything()) # move Sepal.Width to first
# Sepal.Width Sepal.Length Petal.Length Petal.Width Species
# 1 3.5 5.1 1.4 0.2 setosa
# 2 3.0 4.9 1.4 0.2 setosa
iris2 %>% select(-Sepal.Width, Sepal.Width) # move Sepal.Width to last
# Sepal.Length Petal.Length Petal.Width Species Sepal.Width
# 1 5.1 1.4 0.2 setosa 3.5
# 2 4.9 1.4 0.2 setosa 3.0
但是我找不到在给定列之后或之前移动列的任何简便方法。
我在下面发布了一个粗略的解决方案,但:
tidyverse
函数的灵活性来使用数字索引,名称,字符串等... 我相信使用vars
我们还可以移动一列列表,或者移动一组在名称中显示模式的列,等等。但是我对tidyverse
的风格还不太满意编程。
所以我挑战您做得更好/更聪明,或者指出我错过的显而易见的解决方案。
预期输出:
iris2 %>% move_at(Species, Sepal.Width, side = "before")
# Sepal.Length Species Sepal.Width Petal.Length Petal.Width
# 1 5.1 setosa 3.5 1.4 0.2
# 2 4.9 setosa 3.0 1.4 0.2
iris2 %>% move_at(Species, Sepal.Width, side = "after")
# Sepal.Length Sepal.Width Species Petal.Length Petal.Width
# 1 5.1 3.5 setosa 1.4 0.2
# 2 4.9 3.0 setosa 1.4 0.2
答案 0 :(得分:12)
UPDATE:使用rlang::enquo
可以使它更好,然后使用@Zsombor的答案可以使它更短,更优雅。答案末尾的旧解(以R为底)
#' Move column or selection of columns
#'
#' Column(s) described by \code{cols} are moved before (default) or after the reference
#' column described by \code{ref}
#'
#' @param data A \code{data.frame}
#' @param cols unquoted column name or numeric or selection of columns using a select helper
#' @param ref unquoted column name
#' @param side \code{"before"} or \code{"after"}
#'
#' @return A data.frame with reordered columns
#' @export
#'
#' @examples
#' iris2 <- head(iris,2)
#' move(iris2, Species, Sepal.Width)
#' move(iris2, Species, Sepal.Width, "after")
#' move(iris2, 5, 2)
#' move(iris2, 4:5, 2)
#' move(iris2, one_of("Sepal.Width","Species"), Sepal.Width)
#' move(iris2, starts_with("Petal"), Sepal.Width)
move <- function(data, cols, ref, side = c("before","after")){
if(! requireNamespace("dplyr"))
stop("Make sure package 'dplyr' is installed to use function 'move'")
side <- match.arg(side)
cols <- rlang::enquo(cols)
ref <- rlang::enquo(ref)
if(side == "before")
dplyr::select(data,1:!!ref,-!!ref,-!!cols,!!cols,dplyr::everything())
else
dplyr::select(data,1:!!ref,-!!cols,!!cols,dplyr::everything())
}
示例:
iris2 %>% move(Species, Sepal.Width)
# Sepal.Length Species Sepal.Width Petal.Length Petal.Width
# 1 5.1 setosa 3.5 1.4 0.2
# 2 4.9 setosa 3.0 1.4 0.2
iris2 %>% move(Species, Sepal.Width, "after")
# Sepal.Length Sepal.Width Species Petal.Length Petal.Width
# 1 5.1 3.5 setosa 1.4 0.2
# 2 4.9 3.0 setosa 1.4 0.2
iris2 %>% move(5, 2)
# Sepal.Length Species Sepal.Width Petal.Length Petal.Width
# 1 5.1 setosa 3.5 1.4 0.2
# 2 4.9 setosa 3.0 1.4 0.2
iris2 %>% move(4:5, 2)
# Sepal.Length Petal.Width Species Sepal.Width Petal.Length
# 1 5.1 0.2 setosa 3.5 1.4
# 2 4.9 0.2 setosa 3.0 1.4
iris2 %>% move(one_of("Sepal.Width","Species"), Sepal.Width)
# Sepal.Length Sepal.Width Species Petal.Length Petal.Width
# 1 5.1 3.5 setosa 1.4 0.2
# 2 4.9 3.0 setosa 1.4 0.2
iris2 %>% move(starts_with("Petal"), Sepal.Width)
# Sepal.Length Petal.Length Petal.Width Sepal.Width Species
# 1 5.1 1.4 0.2 3.5 setosa
# 2 4.9 1.4 0.2 3.0 setosa
有问题的旧解决方案
这是仅使用基础R编程的简单解决方案:
move_at <- function(data, col, ref, side = c("before","after")){
side = match.arg(side)
col_pos <- match(as.character(substitute(col)),names(data))
ref_pos <- match(as.character(substitute(ref)),names(data))
sorted_pos <- c(col_pos,ref_pos)
if(side =="after") sorted_pos <- rev(sorted_pos)
data[c(setdiff(seq_len(ref_pos-1),col_pos),
sorted_pos,
setdiff(seq_along(data),c(seq_len(ref_pos),col_pos)))]
}
iris2 %>% move_at(Species, Sepal.Width)
# Sepal.Length Species Sepal.Width Petal.Length Petal.Width
# 1 5.1 setosa 3.5 1.4 0.2
# 2 4.9 setosa 3.0 1.4 0.2
iris2 %>% move_at(Species, Sepal.Width, "after")
# Sepal.Length Sepal.Width Species Petal.Length Petal.Width
# 1 5.1 3.5 setosa 1.4 0.2
# 2 4.9 3.0 setosa 1.4 0.2
答案 1 :(得分:6)
不管原始列的顺序如何,这似乎都是可行的(感谢@Moody_Mudskipper的评论):
iris %>% select(1:Sepal.Width, -Species, Species, everything()) %>% head(2)
#> Sepal.Length Sepal.Width Species Petal.Length Petal.Width
#> 1 5.1 3.5 setosa 1.4 0.2
#> 2 4.9 3.0 setosa 1.4 0.2
iris %>% select(1:Sepal.Width, -Sepal.Width, -Species, Species, everything()) %>% head(2)
#> Sepal.Length Species Sepal.Width Petal.Length Petal.Width
#> 1 5.1 setosa 3.5 1.4 0.2
#> 2 4.9 setosa 3.0 1.4 0.2
答案 2 :(得分:3)
仅作记录,另一种解决方案是
library(tidyverse)
data(iris)
iris %>%
select(-Species) %>%
add_column(Specis = iris$Species, .before = "Petal.Length") %>%
head()
#> Sepal.Length Sepal.Width Specis Petal.Length Petal.Width
#> 1 5.1 3.5 setosa 1.4 0.2
#> 2 4.9 3.0 setosa 1.4 0.2
#> 3 4.7 3.2 setosa 1.3 0.2
#> 4 4.6 3.1 setosa 1.5 0.2
#> 5 5.0 3.6 setosa 1.4 0.2
#> 6 5.4 3.9 setosa 1.7 0.4
由reprex package(v0.2.0)于2018-08-31创建。
答案 3 :(得分:2)
我发现了一个非常适合该问题的有趣函数(由{A5C1D2H2I1M1N2O1R2T1编写)moveMe:
source('https://raw.githubusercontent.com/mrdwab/SOfun/master/R/moveMe.R')
head(iris[ moveMe(names(iris), 'Species before Sepal.Width') ], 2)
# Sepal.Length Species Sepal.Width Petal.Length Petal.Width
# 1 5.1 setosa 3.5 1.4 0.2
# 2 4.9 setosa 3.0 1.4 0.2
head(iris[ moveMe(names(iris), 'Species after Sepal.Width') ], 2)
# Sepal.Length Sepal.Width Species Petal.Length Petal.Width
# 1 5.1 3.5 setosa 1.4 0.2
# 2 4.9 3.0 setosa 1.4 0.2
它还允许更复杂的说明:
head(iris[ moveMe(names(iris), 'Species after Sepal.Width; Petal.Width first; Sepal.Length last') ], 2)
# Petal.Width Sepal.Width Species Petal.Length Sepal.Length
# 1 0.2 3.5 setosa 1.4 5.1
# 2 0.2 3.0 setosa 1.4 4.9