是否可以使用R在数据框中交换列?

时间:2011-04-20 00:51:06

标签: r swap

我在数据框中有三个变量,并希望从

交换4列
"dam"   "piglet"   "fdate"   "ssire"

"piglet"   "ssire"   "dam"   "tdate"

我有什么方法可以使用R进行交换?

非常感谢任何帮助。

巴兹

7 个答案:

答案 0 :(得分:31)

dfrm <- dfrm[c("piglet", "ssire", "dam", "tdate")]

OR:

dfrm <- dfrm[ , c("piglet", "ssire", "dam", "tdate")]

答案 1 :(得分:13)

d <- data.frame(a=1:3, b=11:13, c=21:23)
d
#  a  b  c
#1 1 11 21
#2 2 12 22
#3 3 13 23
d2 <- d[,c("b", "c", "a")]
d2
#   b  c a
#1 11 21 1
#2 12 22 2
#3 13 23 3

或者你可以使用索引做同样的事情:

d3 <- d[,c(2, 3, 1)]
d3
#   b  c a
#1 11 21 1
#2 12 22 2
#3 13 23 3

答案 2 :(得分:8)

总结其他帖子,有三种方法可以更改列顺序,以及两种在每种方法中指定索引的方法。

给出样本数据框

dfr <- data.frame(
  dam    = 1:5,
  piglet = runif(5),
  fdate  = letters[1:5],
  ssire  = rnorm(5)
)

Kohske的回答:您可以使用列号

来使用标准的矩阵式索引
dfr[, c(2, 4, 1, 3)]

或使用列名

dfr[, c("piglet", "ssire", "dam", "fdate")]

DWin&amp; Gavin的回答:数据框允许您在指定索引时省略行参数。

dfr[c(2, 4, 1, 3)]
dfr[c("piglet", "ssire", "dam", "fdate")]

PaulHurleyuk的回答:你也可以使用subset

subset(dfr, select = c(2, 4, 1, 3))
subset(dfr, select = c(c("piglet", "ssire", "dam", "fdate")))

答案 3 :(得分:6)

您可以使用子集的'select'参数;

#Assume df contains "dam" "piglet" "fdate" "ssire"

newdf<-subset(df, select=c("piglet", "ssire", "dam", "tdate"))

答案 4 :(得分:2)

我注意到这几乎是一个8岁的问题。但是对于像我一样开始学习R并可能偶然发现此问题的人们,您现在可以使用select()包中一个非常灵活的dplyr函数来完成交换操作,如下所示。

# Install and load the dplyr package
install.packages("dplyr")
library("dplyr")

# Override the existing data frame with the desired column order
df <- select(df, piglet, ssire, dam, tdate)

此方法具有以下优点:

  1. 由于select()不需要将变量名括在引号中,因此您将需要键入更少的内容。
  2. 如果您的数据框具有四个以上的变量,则可以利用选择帮助程序功能(例如starts_with()ends_with()等)来选择多个列,而不必命名每个列并用它们重新排列很轻松。

答案 5 :(得分:1)

相关性注意:针对某些想交换列而不必指定每一列的用户(包括我自己),我写下了这个答案。

TL; DR:此处提供了一种用于数字索引的衬里,并且该函数最后用于恰好交换2个名义和数字索引,而没有使用导入,该函数将正确交换数据帧中的任何两列提供任何大小。还提供了一个功能,该功能允许重新分配任意数量的列(如果使用不当会导致不必要的不​​必要的交换)(请参阅摘要部分中的更多信息并获取功能)


初步解决方案

假设您有一个巨大的(或没有)数据帧DF,并且您只知道要交换的两列的索引,例如1 < n < m < length(DF)。 (同样重要的是,您的列不相邻,即|n-m| > 1在我们的“巨大”数据框中很有可能是这种情况,但对于较小的数据帧则不一定如此;所有简并情况的解决方法都在结束)。 因为它很大,所以有很多列,您不想手动指定其他任何列,或者它不是很大,而您只是 lazy 品味高尚的人在编码中,无论哪种方式,这种单线都可以做到:

    DF <- DF[ c( 1:(n-1), m, (n+1):(m-1), n, (m+1):length(DF) ) ]

每块都是这样的:

    1:(n-1)           # This keeps every column before column `n` in place

    m                 # This places column `m` where column `n` was

    (n+1):(m-1)       # This keeps every column between the two in place

    n                 # This places column `n` where column `m` was

    (m+1):length(DF)  # This keeps every column after column `m` in place

归并简并

由于:运算符的工作原理,即允许这样的“向后范围”,

    > 10:0
      [1] 10  9  8  7  6  5  4  3  2  1  0

我们必须注意nm的选择和位置,因此要遵守我们以前的限制。例如,n < m不会给我们带来任何笼统性(如果其中一列不同,则其中一列必须在另一列之前),但这意味着我们必须注意行中哪一行代码。我们可以做到,这样我们就不必通过以下修改来检查这种情况:

    DF <- DF[ c( 1:(min(n,m)-1), max(n,m), (min(n,m)+1):(max(n,m)-1), min(n,m), (max(n,m)+1):length(DF) ) ]

我们分别将nm的每个实例替换为min(n,m)max(n,m),这意味着即使在以下情况下,也将保留我们代码的正确顺序m > n

min(n,m) == 1max(n,m) == length(DF)(两者同时出现)和|n-m| == 1的情况下,我们将进行不可读较少的美学修饰涉及if\else,而不必检查是否是这种情况。您知道其中一种情况的版本(即,您总是将一些内部列与第一列交换,将一些内部列与最后一列交换,交换第一列和最后一列,或交换两个相邻列),您实际上可以更简洁地表达这些动作,因为它们通常只需要从我们的受限情况中省略部分内容即可:

    # Swapping not the last column with the first column
    # We just got rid of 1:(min(n,m)-1) because it would be invalid and not what we meant
    # since min(n,m) == 1
    # Now we just stick the other column right at the front
    DF <- DF[ c( max(n,m), (min(n,m)+1):(max(n,m)-1), min(n,m), (max(n,m)+1):length(DF) ) ]
    # Also equivalent since we know min(n,m) == 1, for the leftover index i
    DF <- DF[ c( i, 2:(i-1), 1, (i+1):length(DF) ) ]


    # Swapping not the first column with the last column
    # Similarly, we just got rid of (max(n,m)+1):length(DF) because it would also be invalid 
    # and not what we meant since max(n,m) == length(DF)
    # Now we just stick the other column right at the end
    DF <- DF[ c( 1:(min(n,m)-1), max(n,m), (min(n,m)+1):(max(n,m)-1), min(n,m) ) ]
    # Also equivalent since we know max(n,m) == length(DF), for the leftover index, say i
    DF <- DF[ c( 1:(i-1), length(DF), (i+1):(length(DF)-1), i ) ]

    # Swapping the first column with the last column
    DF <- DF[ c( max(n,m), (min(n,m)+1):(max(n,m)-1), min(n,m) ) ]
    # Also equivalent (for if you don't actually know the length beforehand, as assumed 
    # elsewhere)
    DF <- DF[ c( length(DF), 2:(length(DF)-1), 1 ) ]

    # Swapping two interior adjacent columns
    # Here we drop the explicit swap on either side of our middle column segment
    # This is actually enough because then the middle segment becomes a backwards range
    # because we know that `min(n,m) + 1 = max(n,m)`
    # The range is just an ordering of the two adjacent indices from largest to smallest
    DF <- DF[ c( 1:(min(n,m)-1), (min(n,m)+1):(max(n,m)-1), (max(n,m)+1):length(DF) )]

“但是!”,我听到你说,“如果同时发生以上情况中的一种,那就像上面方框中的第三种情况一样!”。没错,如果要在最一般的意义上“交换列”,则为每种情况编码版本会浪费大量时间。


交换任意两列

最简单的方法是使我们的代码同时涵盖所有情况,因为它们基本上都采用相同的策略。我们将使用if\else使我们的代码保持一致:

    DF <- DF[ if (n==m) 1:length(DF) else c( (if (min(n,m)==1) c() else 1:(min(n,m)-1) ), (if (min(n,m)+1 == max(n,m)) (min(n,m)+1):(max(n,m)-1) else c( max(n,m), (min(n,m)+1):(max(n,m)-1), min(n,m))), (if (max(n,m)==length(DF)) c() else (max(n,m)+1):length(DF) ) ) ]

对于可能试图理解或重新创建您的代码(包括您自己)的任何人来说,这是完全不可读的,并且可能非常不友好,因此最好将其包装在函数中。

# A function that swaps the `n` column and `m` column in the data frame DF
swap <- function(DF, n, m)
{
  return (DF[ if (n==m) 1:length(DF) else c( (if (min(n,m)==1) c() else 1:(min(n,m)-1) ), (if (min(n,m)+1 == max(n,m)) (min(n,m)+1):(max(n,m)-1) else c( max(n,m), (min(n,m)+1):(max(n,m)-1), min(n,m))), (if (max(n,m)==length(DF)) c() else (max(n,m)+1):length(DF) ) ) ])
}

一个更健壮的版本,它也可以交换列名并带有不解释的注释:

# Returns data frame object with columns `n` and `m` swapped
# `n` and `m` can be column names, numerical indices, or a heterogeneous pair of both
swap <- function(DF, n, m)
{

  # Of course, first, we want to make sure that n != m,
  # because if they do, we don't need to do anything
  if (n==m) return(DF)

  # Next, if either n or m is a column name, we want to get its index
  # We assume that if they aren't column names, they are indices (integers)
    n <- if (class(n)=="character" & is.na(suppressWarnings(as.integer(n)))) which(colnames(DF)==n) else as.integer(n)
  m <- if (class(m)=="character" & is.na(supressWarnings(as.integer(m)))) which(colnames(DF)==m) else as.integer(m)
  # Make sure each index is actually valid
  if (!(1<=n & n<=length(DF))) stop( "`n` represents invalid index!" )
  if (!(1<=m & m<=length(DF))) stop( "`m` represents invalid index!" )

  # Also, for readability, lets go ahead and set which column is earlier, and which is later
  earlier <- min(n,m)
  later <- max(n,m)

  # This constructs the first third of the indices 
  # These are the columns that, if any, come before the earlier column you are swapping
  firstThird <- if ( earlier==1 ) c() else 1:(earlier-1)

  # This constructs the last third of the the indices
  # These are the columns, if any, that come after the later column you are swapping
  lastThird <- if ( later==length(DF) ) c() else (later+1):length(DF) 

  # This checks if the columns to be swapped are adjacent and then constructs the 
  # secondThird accordingly
  if ( earlier+1 == later )
  {
    # Here; the second third is a list of the two columns ordered from later to earlier
    secondThird <- (earlier+1):(later-1)
  }
  else
  {
    # Here; the second third is a list of 
    # the later column you want to swap
    # the columns in between
    # and then the earlier column you want to swap
    secondThird <- c( later, (earlier+1):(later-1), earlier)
  }

  # Now we assemble our indices and return our permutation of DF
  return (DF[ c( firstThird, secondThird, lastThird ) ])
}

为了简化归还程序,减少了空间成本,它是一种无注释版本,该版本可检查索引的有效性并可以处理列名,即在尽可能接近的最小空间范围内进行所有操作(是的,您可以矢量化,使用ifelse(...),将执行两项检查,但是随后您必须将向量解压缩回n,m或更改最后一行的写入方式):

 swap <- function(DF, n, m)
{
  n <- if (class(n)=="character" & is.na(suppressWarnings(as.integer(n)))) which(colnames(DF)==n) else as.integer(n)
  m <- if (class(m)=="character" & is.na(suppressWarnings(as.integer(m)))) which(colnames(DF)==m) else as.integer(m)

  if (!(1<=n & n<=length(DF))) stop( "`n` represents invalid index!" )
  if (!(1<=m & m<=length(DF))) stop( "`m` represents invalid index!" )

  return (DF[ if (n==m) 1:length(DF) else c( (if (min(n,m)==1) c() else 1:(min(n,m)-1) ), (if (min(n,m)+1 == max(n,m)) (min(n,m)+1):(max(n,m)-1) else c( max(n,m), (min(n,m)+1):(max(n,m)-1), min(n,m))), (if (max(n,m)==length(DF)) c() else (max(n,m)+1):length(DF) ) ) ])
}

排列(或如何具体执行所问的问题以及更多!)

使用我们的swap函数,我们可以尝试实际完成最初提出的问题。最简单的方法是构建一个函数,该函数利用各种异构参数附带的真正强大的功能。创建映射:

mapping <- data.frame( "piglet" = 1, "ssire" = 2, "dam" = 3, "tdate" = 4)

在原始问题的情况下,这些都是我们原始数据框中的所有列,但是我们将构建一个不必是这种情况的函数:

# A function that takes two data frames, one with actual data: DF, and the other with a 
# rearrangement of the columns: R
# R must be structured so that colnames(R) is a subset of colnames(DF)
# Alternatively, R can be structured so that 1 <= as.integer(colnames(R)) <= length(DF)
# Further, 1 <= R$column <= length(DF), and length(R$column) == 1
# These structural requirements on R are not checked
# This is for brevity and because most likely R has been created specifically for use with
# this function
rearrange <- function(DF, R)
{
  for (col in colnames(R))
  {
    DF <- swap(DF, col, R[col])
  }

  return (DF)
}

等等,就是这样吗?对。这会将每个列名称交换到适当的位置。这种swap的强大功能来自于使用异类参数的情况,这意味着我们可以指定要放在某处的 moving 列名,并且只要我们只尝试在其中放置一列即可每个位置(应该的位置),只要将该列放在该位置,它就不会再移动。这意味着即使以后的交换似乎可以撤消先前的放置,但异构参数也确保不会发生这种情况,因此,此外,映射中列的顺序也无关紧要。这是非常好的质量,因为这意味着我们不会在整个过程中过多地处理整个“组织数据”问题。您只需要确定要将每个列发送到的位置即可。

好的,好的,有一个陷阱。如果在执行此操作时未重新分配整个数据框,则将发生多余的交换,这意味着如果重新排列未“关闭”的列子集,即并非每个列名都有一个重新排列中表示的索引,那么您未明确表示要移动的其他列可能会移动到它们不完全属于的其他位置。这可以通过非常仔细地创建映射或简单地使用映射到其他数字索引的数字索引来解决。在后一种情况下,这不能解决问题,但可以更清楚地显示正在发生的交换以及以什么顺序进行,因此计划重新安排更为明确,因此不太可能导致出现问题的多余交换。


摘要

您可以使用我们构建的swap函数来成功地恰好交换两列,或者使用rearrange函数使用“重新排列”数据框来指定要移动的每个列名的发送位置。对于rearrange函数,如果为每个列名称选择的任何放置都未被指定的列之一占用(即不在colnames(R)中),则 多余交换可以并且很可能发生 (唯一不会发生的情况是,每个多余的交换都有一个伙伴多余的交换在结束之前将其撤销。如上所述,这极不可能偶然发生,但可以通过构造映射来在实践中实现此结果。

swap <- function(DF, n, m)
{
  n <- if (class(n)=="character" & is.na(suppressWarnings(as.integer(n)))) which(colnames(DF)==n) else as.integer(n)
  m <- if (class(m)=="character" & is.na(suppressWarnings(as.integer(m)))) which(colnames(DF)==m) else as.integer(m)

  if (!(1<=n & n<=length(DF))) stop( "`n` represents invalid index!" )
  if (!(1<=m & m<=length(DF))) stop( "`m` represents invalid index!" )

  return (DF[ if (n==m) 1:length(DF) else c( (if (min(n,m)==1) c() else 1:(min(n,m)-1) ), (if (min(n,m)+1 == max(n,m)) (min(n,m)+1):(max(n,m)-1) else c( max(n,m), (min(n,m)+1):(max(n,m)-1), min(n,m))), (if (max(n,m)==length(DF)) c() else (max(n,m)+1):length(DF) ) ) ])
}

rearrange <- function(DF, R)
{
  for (col in colnames(R))
  {
    DF <- swap(DF, col, R[col])
  }

  return (DF)
}

答案 6 :(得分:0)

我迅速编写了一个函数,该函数采用向量v和要交换的列索引a和b。

swappy = function(v,a,b){  # where v is a dataframe, a and b are the 
columns indexes to swap

name = deparse(substitute(v))

helpy = v[,a]
v[,a] = v[,b]
v[,b] = helpy


name1 = colnames(v)[a] 
name2 = colnames(v)[b] 

colnames(v)[a] = name2
colnames(v)[b] = name1

assign(name,value = v , envir =.GlobalEnv)
}