在数据框中两列之间添加(插入)一列

125
我有一个数据框,其中包含列a、b和c。我想在b和c之间添加一个新的列d。 我知道可以使用cbind将d添加到末尾,但如何在两列之间插入它呢?

也许这是你想要的:http://r.789695.n4.nabble.com/How-to-insert-a-column-in-a-data-frame-td883724.html - Mark Miller
1
dplyr包中的mutate()函数是否允许像这个问题所述的那样添加列? - marbel
18个回答

116

我建议你使用tibble包中的add_column()函数。

library(tibble)
dataset <- data.frame(a = 1:5, b = 2:6, c=3:7)
add_column(dataset, d = 4:8, .after = 2)

请注意,您可以使用列名代替列索引:

add_column(dataset, d = 4:8, .after = "b")

如果更方便的话,可以使用参数.before而不是.after

add_column(dataset, d = 4:8, .before = "c")

9
我删除了名字的提及。这似乎并没有增加太多内容,而且虽然 Hadley 被列为 tibble 包的一个作者,但 Kirill Müller 被列为创建者和维护者 - Gregor Thomas

55
在您的新列中添加:
df$d <- list/data

然后您可以重新排列它们。

df <- df[, c("a", "b", "d", "c")]

1
我发现在使用setcolorder时,结合列号(而不是列名)进行重新排序也非常有用,因为一旦列数变得非常大,你就可以开始使用seqrep来完成大部分工作。此外,还可以使用算术运算符。例如:setcolorder(data, c(1, (num_cols -2), (num_cols -1), num_cols, seq(from = 2, to = (num_cols - 3)))) - n1k31t4
3
我应该提醒一下,setcolorder 是针对 data.table 而不是 data.frame 的! - n1k31t4

26

您可以使用[,]重新排序列,或按您想要的顺序呈现列。

d <- data.frame(a=1:4, b=5:8, c=9:12)
target <- which(names(d) == 'b')[1]
cbind(d[,1:target,drop=F], data.frame(d=12:15), d[,(target+1):length(d),drop=F])

  a b  d  c
1 1 5 12  9
2 2 6 13 10
3 3 7 14 11
4 4 8 15 12

17
这是一个很好的回答。但我不得不承认,这也是为什么R对于初学者来说可能很困难的一个很好的例子。 - tumultous_rooster
4
话虽如此,我认为@ashah57在下面给出了一个更简单、更清晰的答案。在这种情况下,不需要过于花哨。 - tumultous_rooster

18

假设b总是紧随着c,这段代码将在b后面添加一列,无论b在数据框中的位置如何。

> test <- data.frame(a=1,b=1,c=1)
> test
  a b c
1 1 1 1

> bspot <- which(names(test)=="b")

> data.frame(test[1:bspot],d=2,test[(bspot+1):ncol(test)])
  a b d c
1 1 1 2 1

或者更自然地说:

data.frame(append(test, list(d=2), after=match("b", names(test))))

9
创建一个示例数据框并向其中添加一列。
df = data.frame(a = seq(1, 3), b = seq(4,6), c = seq(7,9))
df['d'] <- seq(10,12)
df

  a b c  d
1 1 4 7 10
2 2 5 8 11
3 3 6 9 12

按列索引重新排列

df[, colnames(df)[c(1:2,4,3)]]

或者按列名排序

df[, c('a', 'b', 'd', 'c')]

结果是:
  a b  d c
1 1 4 10 7
2 2 5 11 8
3 3 6 12 9

5
这里有一个快速而简单的方法在数据框中的特定位置插入一列。在我的案例中,原始数据框有5列:c1、c2、c3、c4、c5,我将在c2c3之间插入新列c2b
1)首先,让我们创建测试数据框:
> dataset <- data.frame(c1 = 1:5, c2 = 2:6, c3=3:7, c4=4:8, c5=5:9)
> dataset
  c1 c2 c3 c4 c5
1  1  2  3  4  5
2  2  3  4  5  6
3  3  4  5  6  7
4  4  5  6  7  8
5  5  6  7  8  9

2) 在我们的数据框中添加新列c2b

> dataset$c2b <- 10:14
> dataset
  c1 c2 c3 c4 c5 c2b
1  1  2  3  4  5  10
2  2  3  4  5  6  11
3  3  4  5  6  7  12
4  4  5  6  7  8  13
5  5  6  7  8  9  14

3) 基于列索引重新排序数据框。在我的情况下,我想在现有的第2列和第3列之间插入新的第6列。我通过使用向量c(1:2, 6, 3:5)来定位我的数据框上的列,这等同于c(1, 2, 6, 3, 4, 5)

> dataset <- dataset[,c(1:2, 6, 3:5)]
> dataset
  c1 c2 c2b c3 c4 c5
1  1  2  10  3  4  5
2  2  3  11  4  5  6
3  3  4  12  5  6  7
4  4  5  13  6  7  8
5  5  6  14  7  8  9

在这里!


4
你想要在由列 x 和 y 定义的旧数据框(old.df)中添加列 z。
z = rbinom(1000, 5, 0.25)
old.df <- data.frame(x = c(1:1000), y = rnorm(1:1000))
head(old.df)

定义一个新的数据框,称为new.df。

new.df <- data.frame(x = old.df[,1], z, y = old.df[,2])
head(new.df)

4

简单解决方案。在一个有5列的数据框中,如果你想在第3列和第4列之间插入另一列...

tmp <- data[, 1:3]
tmp$example <- NA # or any value.
data <- cbind(tmp, data[, 4:5]

2

就算只是一点点价值,我也写了一个函数来完成这个任务:

[已删除]


现在我已经更新了这个函数,增加了beforeafter功能,并将place默认设置为1。它还兼容数据表格:

#####
# FUNCTION: InsertDFCol(colName, colData, data, place = 1, before, after)
# DESCRIPTION: Takes in a data, a vector of data, a name for that vector and a place to insert this vector into
# the data frame as a new column. If you put place = 3, the new column will be in the 3rd position and push the current
# 3rd column up one (and each subsuquent column up one). All arguments must be set. Adding a before and after
# argument that will allow the user to say where to add the new column, before or after a particular column.
# Please note that if before or after is input, it WILL override the place argument if place is given as well. Also, place
# defaults to adding the new column to the front.
#####

InsertDFCol <- function(colName, colData, data, place = 1, before, after) {

  # A check on the place argument.
  if (length(names(data)) < place) stop("The place argument exceeds the number of columns in the data for the InsertDFCol function. Please check your place number")
  if (place <= 0 & (!missing(before) | !(missing(after)))) stop("You cannot put a column into the 0th or less than 0th position. Check your place argument.")
  if (place %% 1 != 0 & (!missing(before) | !(missing(after)))) stop("Your place value was not an integer.")
  if (!(missing(before)) & !missing(after)) stop("You cannot designate a before AND an after argument in the same function call. Please use only one or the other.")

  # Data Table compatability.
  dClass <- class(data)
  data <- as.data.frame(data)

  # Creating booleans to define whether before or after is given.
  useBefore <- !missing(before)
  useAfter <- !missing(after)

  # If either of these are true, then we are using the before or after argument, run the following code.
  if (useBefore | useAfter) {

    # Checking the before/after argument if given. Also adding regular expressions.
    if (useBefore) { CheckChoice(before, names(data)) ; before <- paste0("^", before, "$") }
    if (useAfter) { CheckChoice(after, names(data)) ; after <- paste0("^", after, "$") }

    # If before or after is given, replace "place" with the appropriate number.
    if (useBefore) { newPlace <- grep(before, names(data)) ; if (length(newPlace) > 1) { stop("Your before argument matched with more than one column name. Do you have duplicate column names?!") }}
    if (useAfter) { newPlace <- grep(after, names(data)) ; if (length(newPlace) > 1) { stop("Your after argument matched with more than one column name. Do you have duplicate column names?!") }}
    if (useBefore) place <- newPlace # Overriding place.
    if (useAfter) place <- newPlace + 1 # Overriding place.

  }

  # Making the new column.
  data[, colName] <- colData

  # Finding out how to reorder this.
  # The if statement handles the case where place = 1.
  currentPlace <- length(names(data)) # Getting the place of our data (which should have been just added at the end).
  if (place == 1) {

    colOrder <- c(currentPlace, 1:(currentPlace - 1))

  } else if (place == currentPlace) { # If the place to add the new data was just at the end of the data. Which is stupid...but we'll add support anyway.

    colOrder <- 1:currentPlace

  } else { # Every other case.

    firstHalf <- 1:(place - 1) # Finding the first half on columns that come before the insertion.
    secondHalf <- place:(currentPlace - 1) # Getting the second half, which comes after the insertion.
    colOrder <- c(firstHalf, currentPlace, secondHalf) # Putting that order together.

  }

  # Reordering the data.
  data <- subset(data, select = colOrder)

  # Data Table compatability.
  if (dClass[1] == "data.table") data <- as.data.table(data)

  # Returning.
  return(data)

}

我发现我还没有包括CheckChoice:
#####
# FUNCTION: CheckChoice(names, dataNames, firstWord == "Oops" message = TRUE)                                                                                               
# DESCRIPTION: Takes the column names of a data frame and checks to make sure whatever "choice" you made (be it 
# your choice of dummies or your choice of chops) is actually in the data frame columns. Makes troubleshooting easier. 
# This function is also important in prechecking names to make sure the formula ends up being right. Use it after 
# adding in new data to check the "choose" options. Set firstWord to the first word you want said before an exclamation point.
# The warn argument (previously message) can be set to TRUE if you only want to 
#####

CheckChoice <- function(names, dataNames, firstWord = "Oops", warn = FALSE) {

  for (name in names) {

    if (warn == TRUE) { if(!(name %in% dataNames)) { warning(paste0(firstWord, "! The column/value/argument, ", name, ", was not valid OR not in your data! Check your input! This is a warning message of that!")) } }
    if (warn == FALSE) { if(!(name %in% dataNames)) { stop(paste0(firstWord, "! The column/value/argument, " , name, ", was not valid OR not in your data! Check your input!")) } }

  }
}

2

我会简单地使用cbind()函数实现这个操作:

> df <- data.frame(a=1:5,
+                  b=10:14,
+                  c=rep(0,5),
+                  d=7:11)
> 
> z <- LETTERS[1:5]
> df <- cbind(df[,1:2], z, df[,3:4]) # Puts the z column between 2nd and 3rd column of df
> df
  a  b z c  d
1 1 10 A 0  7
2 2 11 B 0  8
3 3 12 C 0  9
4 4 13 D 0 10
5 5 14 E 0 11

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接