在 data.frame 中的两列之间添加(插入)一列

我有一个数据框架,它有 a、 b 和 c 列,我想在 b 和 c 之间添加一个新的列 d。

我知道我可以使用 Cbind在最后添加 d,但是我怎样才能在两列之间添加 插入呢?

168124 次浏览

您可以使用[对列进行重新排序,也可以按照所需的顺序显示列。

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

假设 c总是紧跟在 b之后,这段代码将在 b之后添加一列,而不管 b在 data.frame 中的哪个位置。

> 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

Or possibly more naturally:

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

加入你的新专栏:

df$d <- list/data

然后你可以重新订购。

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

您希望将列 z 添加到旧的数据框架(old.df) defined by columns x and y.

z = rbinom(1000, 5, 0.25)
old.df <- data.frame(x = c(1:1000), y = rnorm(1:1000))
head(old.df)

Define a new data frame called new.df

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

`

data1 <- data.frame(col1=1:4, col2=5:8, col3=9:12)
row.names(data1) <- c("row1","row2","row3","row4")
data1
data2 <- data.frame(col1=21:24, col2=25:28, col3=29:32)
row.names(data2) <- c("row1","row2","row3","row4")
data2
insertPosition = 2
leftBlock <- unlist(data1[,1:(insertPosition-1)])
insertBlock <- unlist(data2[,1:length(data2[1,])])
rightBlock <- unlist(data1[,insertPosition:length(data1[1,])])
newData <- matrix(c(leftBlock, insertBlock, rightBlock), nrow=length(data1[,1]), byrow=FALSE)
newData

`

不管怎样,我编写了一个函数来实现这一点:

[removed]


我现在已经用 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!")) } }


}
}

这里有一种快速而简单的方法,可以将列插入到数据框架的特定位置。在我的示例中,原始数据框架中有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

那里!

这个函数在数据框架中所有预先存在的列之间插入一个零列。

insertaCols<-function(dad){
nueva<-as.data.frame(matrix(rep(0,nrow(daf)*ncol(daf)*2 ),ncol=ncol(daf)*2))
for(k in 1:ncol(daf)){
nueva[,(k*2)-1]=daf[,k]
colnames(nueva)[(k*2)-1]=colnames(daf)[k]
}
return(nueva)
}

R 没有指定新列添加位置的功能。例如,mtcars$mycol<-'foo'。它总是作为最后一列添加。使用其他方法(例如,dplyr's select()) ,你可以移动麦考尔到一个理想的位置。这并不理想,R 可能想在未来尝试改变这一点。

创建一个示例 data.frame 并向其添加一列。

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

Rearrange by column index

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

或列名

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

The result is

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

我建议您使用 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")

下面是如何将列从最后一个位置移动到第一个位置的示例。它结合了 [ncol。我认为在这里为忙碌的读者提供一个简短的答案是有用的:

d = mtcars
d[, c(ncol(d), 1:(ncol(d)-1))]

你可以像下面这样做

df <- data.frame(a=1:4, b=5:8, c=9:12)
df['d'] <- seq(10,13)
df <- df[,c('a','b','d','c')]

Easy solution. In a data frame with 5 columns, If you want insert another column between 3 and 4...

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

您可以使用 append()函数将项目插入向量或列表(数据框架是列表):

df <- data.frame(a=c(1,2), b=c(3,4), c=c(5,6))


df <- as.data.frame(append(df, list(d=df$b+df$c), after=2))

或者,如果要按名称指定位置,请使用 which:

df <- as.data.frame(append(df, list(d=df$b+df$c), after=which(names(df)=="b")))
df <- data.frame(a=c(1,2), b=c(3,4), c=c(5,6))
df %>%
mutate(d= a/2) %>%
select(a, b, d, c)

结果

  a b   d c
1 1 3 0.5 5
2 2 4 1.0 6

我建议在 dplyr::mutate之后使用 dplyr::select。它有很多辅助工具来选择/取消选择列的子集。

在这个问题的上下文中,您选择的顺序将反映在输出 data.frame 中。

当你不能假设列 bc之前,你可以使用 match来找到两者的列号,min来得到较低的列号,而 seq_len来得到一个序列,直到这一列。然后,您可以首先使用这个索引作为 确定子集,然后放置新的列 d,然后再次使用序列作为 没有子集。

i <- seq_len(min(match(c("b", "c"), colnames(x))))
data.frame(x[i], d, x[-i])
#cbind(x[i], d, x[-i]) #Alternative
#  a b  d c
#1 1 4 10 7
#2 2 5 11 8
#3 3 6 12 9

如果你知道 b列在 c之前,你可以把新的 d列放在 b之后:

i <- seq_len(match("b", colnames(x)))
data.frame(x[i], d, x[-i])
#  a b  d c
#1 1 4 10 7
#2 2 5 11 8
#3 3 6 12 9

Data:

x <- data.frame(a = 1:3, b = 4:6, c = 7:9)
d <- 10:12

我会简单地使用 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