Dodaj (wstaw) kolumnę między dwiema kolumnami w data.frame


89

Mam ramkę danych zawierającą kolumny a, b i c. Chciałbym dodać nową kolumnę d między b i c.

Wiem, że mógłbym po prostu dodać d na końcu, używając cbind, ale jak mogę wstawić go między dwie kolumny?


Może to robi to, co chcesz: r.789695.n4.nabble.com/…
Mark Miller

1
czy funkcja mutate () w pakiecie dplyr pozwala na dodawanie kolumn, jak podano w tym pytaniu?
marbel

Odpowiedzi:


83

Proponuję skorzystać z funkcji add_column()z tibblepakietu.

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

Zauważ, że możesz użyć nazw kolumn zamiast indeksu kolumn:

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

Lub użyj argumentu .beforezamiast, .afterjeśli wygodniej.

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

6
Usunąłem zrzucanie nazw. Nie wydaje się, aby dodać dużo, a podczas Hadley jest wymieniony jako na autora pakietu Kirill Müller jest wymieniony jako twórcy i opiekuna .
Gregor Thomas

39

Dodaj w nowej kolumnie:

df$d <- list/data

Następnie możesz je ponownie zamówić.

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

1
Uważam, że zmiana kolejności używania setcolorderw połączeniu z numerami kolumn (w przeciwieństwie do ich nazw) również jest bardzo przydatna, ponieważ gdy liczba kolumn stanie się bardzo duża, możesz zacząć używać seqi repwykonywać większość pracy. Można również stosować operatory arytmetyczne. Np.setcolorder(data, c(1, (num_cols -2), (num_cols -1), num_cols, seq(from = 2, to = (num_cols - 3))))
n1k31t4

1
Powinienem wspomnieć, że setcolorderjest przeznaczony dla tabeli danych, a nie ramki danych!
n1k31t4

21

Możesz zmienić kolejność kolumn za pomocą [lub przedstawić kolumny w żądanej kolejności.

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

13
To świetna odpowiedź. Ale muszę przyznać, że jest to również świetny przykład tego, dlaczego R może być trudny dla początkujących.
tumultous_rooster

2
Biorąc to pod uwagę, myślę, że @ ashah57 ma znacznie prostszą i czystszą odpowiedź poniżej. Nie ma potrzeby, aby coś takiego wyglądało jak to.
tumultous_rooster

12

Zakładając, że czawsze następuje bto natychmiast , ten kod doda kolumnę po każdym bmiejscu bw 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

Lub bardziej naturalnie:

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

5

Utwórz przykładową ramkę data.frame i dodaj do niej kolumnę.

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

Zmień kolejność według indeksu kolumn

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

lub według nazwy kolumny

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

Wynik to

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

3

Chciałbyś dodać kolumnę z do starej ramki danych (stary.df) zdefiniowanej przez kolumny x i y.

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

Zdefiniuj nową ramkę danych o nazwie new.df

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

3

Oto szybki i brudny sposób wstawiania kolumny w określonej pozycji w ramce danych. W moim przypadku mam 5 kolumn w oryginalnej ramce danych: c1, c2, c3, c4, c5i wstawię nową kolumnę c2bmiędzy c2a c3.

1) Najpierw utwórzmy ramkę danych testowych:

> 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) Dodaj nową kolumnę c2bna końcu naszej ramki danych:

> 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) Zmień kolejność ramki danych na podstawie indeksów kolumn. W moim przypadku chcę wstawić nową kolumnę (6) między istniejące kolumny 2 i 3. Robię to, adresując kolumny w mojej ramce danych za pomocą wektora, c(1:2, 6, 3:5)który jest równoważny 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

Tam!


2

Na co warto napisałem funkcję, aby to zrobić:

[oddalony]


Mam teraz zaktualizowane z tej funkcji beforei afterfunkcjonalność i zalegających placena 1. Posiada również tabeli danych kompatybilność:

#####
# 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)

}

Zdałem sobie sprawę, że nie uwzględniłem też 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

Proste rozwiązanie. W ramce danych z 5 kolumnami, jeśli chcesz wstawić kolejną kolumnę między 3 a 4 ...

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

1

Ta funkcja wstawia jedną kolumnę zerową między wszystkie wcześniej istniejące kolumny w ramce danych.

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)   
  }

1

Oto przykład, jak przenieść kolumnę z ostatniej na pierwszą pozycję. Łączy się [z ncol. Pomyślałem, że przydałaby się tutaj bardzo krótka odpowiedź dla zajętego czytelnika:

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

1

Możesz użyć tej append()funkcji, aby wstawić elementy do wektorów lub list (ramki danych to listy). Po prostu:

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))

Lub, jeśli chcesz określić pozycję według nazwy, użyj which:

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

0

`

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

`


0

R nie ma funkcji określania miejsca dodawania nowej kolumny. Np mtcars$mycol<-'foo'. Zawsze jest dodawany jako ostatnia kolumna. Używając innych środków (np. dplyr's select()) Możesz przesunąć mykol do żądanej pozycji. To nie jest idealne rozwiązanie i R może chcieć to zmienić w przyszłości.


Tak, ma appendfunkcję.
Simon Woodward

0

Możesz to zrobić jak poniżej -

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

0
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)

wyniki

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

Proponuję użyć dplyr::selectpo dplyr::mutate. Ma wielu pomocników do zaznaczania / usuwania zaznaczenia podzbioru kolumn.

W kontekście tego pytania kolejność wyboru zostanie odzwierciedlona w wyjściowej ramce data.frame.


0

Kiedy nie możesz założyć, że kolumna bwystępuje wcześniej c, możesz użyć matchdo znalezienia numeru kolumny obu, minaby uzyskać niższy numer kolumny i seq_lenuzyskać sekwencję do tej kolumny. Następnie możesz najpierw użyć tego indeksu jako podzbioru dodatniego , następnie umieścić nową kolumnę, da następnie użyć sekwencji ponownie jako podzbioru ujemnego .

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

W przypadku, gdy wiesz, że kolumna bpojawi się przed cumieszczeniem nowej kolumny dpo 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

Dane:

x <- data.frame(a = 1:3, b = 4:6, c = 7:9)
d <- 10:12
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.