Thêm một cột vào data.frame


115

Tôi có data.frame bên dưới. Tôi muốn thêm một cột phân loại dữ liệu của tôi theo cột 1 ( h_no) theo cách đó chuỗi h_no 1,2,3,4 đầu tiên là lớp 1, chuỗi thứ hai của h_no(1 đến 7) là lớp 2, v.v. chẳng hạn như được chỉ ra trong cột cuối cùng.

h_no  h_freq  h_freqsq
1     0.09091 0.008264628 1
2     0.00000 0.000000000 1
3     0.04545 0.002065702 1
4     0.00000 0.000000000 1  
1     0.13636 0.018594050 2
2     0.00000 0.000000000 2
3     0.00000 0.000000000 2
4     0.04545 0.002065702 2
5     0.31818 0.101238512 2
6     0.00000 0.000000000 2
7     0.50000 0.250000000 2 
1     0.13636 0.018594050 3 
2     0.09091 0.008264628 3
3     0.40909 0.167354628 3
4     0.04545 0.002065702 3

Câu trả lời:


155

Bạn có thể thêm một cột vào dữ liệu của mình bằng nhiều kỹ thuật khác nhau. Các trích dẫn bên dưới đến từ phần "Chi tiết" của văn bản trợ giúp có liên quan , [[.data.frame.

Khung dữ liệu có thể được lập chỉ mục ở một số chế độ. Khi [[[được sử dụng với một chỉ mục vectơ đơn ( x[i]hoặc x[[i]]), chúng lập chỉ mục khung dữ liệu như thể nó là một danh sách.

my.dataframe["new.col"] <- a.vector
my.dataframe[["new.col"]] <- a.vector

Phương thức data.frame được $coi xnhư một danh sách

my.dataframe$new.col <- a.vector

Khi [[[được sử dụng với hai chỉ số ( x[i, j]x[[i, j]]), chúng hoạt động giống như lập chỉ mục một ma trận

my.dataframe[ , "new.col"] <- a.vector

Vì phương thức cho data.framegiả định rằng nếu bạn không chỉ định xem bạn đang làm việc với cột hay hàng, nó sẽ giả định rằng bạn có nghĩa là cột.


Đối với ví dụ của bạn, điều này sẽ hoạt động:

# make some fake data
your.df <- data.frame(no = c(1:4, 1:7, 1:5), h_freq = runif(16), h_freqsq = runif(16))

# find where one appears and 
from <- which(your.df$no == 1)
to <- c((from-1)[-1], nrow(your.df)) # up to which point the sequence runs

# generate a sequence (len) and based on its length, repeat a consecutive number len times
get.seq <- mapply(from, to, 1:length(from), FUN = function(x, y, z) {
            len <- length(seq(from = x[1], to = y[1]))
            return(rep(z, times = len))
         })

# when we unlist, we get a vector
your.df$group <- unlist(get.seq)
# and append it to your original data.frame. since this is
# designating a group, it makes sense to make it a factor
your.df$group <- as.factor(your.df$group)


   no     h_freq   h_freqsq group
1   1 0.40998238 0.06463876     1
2   2 0.98086928 0.33093795     1
3   3 0.28908651 0.74077119     1
4   4 0.10476768 0.56784786     1
5   1 0.75478995 0.60479945     2
6   2 0.26974011 0.95231761     2
7   3 0.53676266 0.74370154     2
8   4 0.99784066 0.37499294     2
9   5 0.89771767 0.83467805     2
10  6 0.05363139 0.32066178     2
11  7 0.71741529 0.84572717     2
12  1 0.10654430 0.32917711     3
13  2 0.41971959 0.87155514     3
14  3 0.32432646 0.65789294     3
15  4 0.77896780 0.27599187     3
16  5 0.06100008 0.55399326     3

Sự khác biệt giữa hai phương pháp thêm cột cuối cùng là gì?
huon

2
@ huon-dbaupp phương thức có dấu phẩy là rõ ràng và cũng sẽ hoạt động trên ma trận, trong khi phương thức cuối cùng chỉ hoạt động trên data.frames. Nếu không có dấu phẩy nào được cung cấp, R giả sử bạn có nghĩa là cột.
Roman Luštrik

12

Dễ dàng: Khung dữ liệu của bạn là A

b <- A[,1]
b <- b==1
b <- cumsum(b)

Sau đó, bạn nhận được cột b.


Đẹp và ngắn gọn. Tôi sẽ chỉ thay đổi phần tử cuối cùng để thay vì là cumsum(b) -> bkết quả sẽ được thêm trực tiếp dưới dạng một cột vào khung dữ liệu ban đầu, giống như vậy A$groups <- cumsum(b).
A5C1D2H2I1M1N2O1R2T1

cumsum(b)sẽ cung cấp cho bạn một vectơ có độ dài 3, hay tôi thiếu thứ gì đó?
Roman Luštrik

@ RomanLuštrik, hãy xem giải pháp của dbaupp giải thích cách cumsum sẽ hoạt động trong trường hợp này.
A5C1D2H2I1M1N2O1R2T1

2
@ RomanLuštrik, Giải pháp này có thể được viết lại rất độc đáo trong một dòng. Sử dụng your.dfdữ liệu của mình , bạn có thể chỉ cần thực hiện your.df$group = cumsum(your.df[, 1]==1)để lấy cột nhóm mới của mình.
A5C1D2H2I1M1N2O1R2T1

7

Nếu tôi hiểu câu hỏi một cách chính xác, bạn muốn phát hiện khi h_nonào giá trị không tăng và sau đó tăng class. (Tôi sẽ đi qua cách tôi đã giải quyết vấn đề này, có một chức năng tự ở cuối.)

Đang làm việc

Chúng tôi chỉ quan tâm đến h_nocột vào lúc này, vì vậy chúng tôi có thể trích xuất cột đó từ khung dữ liệu:

> h_no <- data$h_no

Chúng tôi muốn phát hiện khi h_nonào không tăng, điều này chúng tôi có thể làm bằng cách tìm ra khi sự khác biệt giữa các phần tử liên tiếp là âm hoặc bằng không. R cung cấp diffhàm cho chúng ta vectơ khác biệt:

> d.h_no <- diff(h_no)
> d.h_no
 [1]  1  1  1 -3  1  1  1  1  1  1 -6  1  1  1

Một khi chúng ta có điều đó, việc tìm ra những cái không tích cực là một vấn đề đơn giản:

> nonpos <- d.h_no <= 0
> nonpos
 [1] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE
[13] FALSE FALSE

Trong R, TRUEFALSEvề cơ bản giống với 10, vì vậy nếu chúng ta nhận được tổng tích lũy của nonpos, nó sẽ tăng 1 trong (gần như) các điểm thích hợp. Các cumsumchức năng (mà về cơ bản là trái ngược với diff) có thể làm điều này.

> cumsum(nonpos)
 [1] 0 0 0 1 1 1 1 1 1 1 2 2 2 2

Tuy nhiên, có hai vấn đề: một số quá nhỏ; và, chúng tôi đang thiếu phần tử đầu tiên (phải có bốn phần tử trong lớp đầu tiên).

Vấn đề đầu tiên chỉ đơn giản được giải quyết: 1+cumsum(nonpos). Và phần thứ hai chỉ yêu cầu thêm a 1vào phía trước của vectơ, vì phần tử đầu tiên luôn nằm trong lớp 1:

 > classes <- c(1, 1 + cumsum(nonpos))
 > classes
  [1] 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3

Bây giờ, chúng tôi có thể đính kèm nó trở lại khung dữ liệu của chúng tôi với cbind(bằng cách sử dụng class=cú pháp, chúng tôi có thể đặt classtiêu đề cho cột ):

 > data_w_classes <- cbind(data, class=classes)

data_w_classesbây giờ chứa kết quả.

Kết quả cuối cùng

Chúng ta có thể nén các dòng lại với nhau và gói tất cả lại thành một hàm để dễ sử dụng hơn:

classify <- function(data) {
   cbind(data, class=c(1, 1 + cumsum(diff(data$h_no) <= 0)))
}

Hoặc, vì nó có ý nghĩa đối classvới một yếu tố:

classify <- function(data) {
   cbind(data, class=factor(c(1, 1 + cumsum(diff(data$h_no) <= 0))))
}

Bạn sử dụng một trong hai chức năng như:

> classified <- classify(data) # doesn't overwrite data
> data <- classify(data) # data now has the "class" column

(Phương pháp giải quyết vấn đề này rất tốt vì nó tránh lặp lại rõ ràng, thường được khuyến nghị cho R và tránh tạo ra nhiều vectơ trung gian và danh sách, v.v. Và cũng khá gọn gàng khi nó có thể được viết trên một dòng :))


2

Ngoài câu trả lời của Roman, một cái gì đó như thế này có thể còn đơn giản hơn. Lưu ý rằng tôi chưa thử nghiệm nó vì tôi không có quyền truy cập vào R ngay bây giờ.

# Note that I use a global variable here
# normally not advisable, but I liked the
# use here to make the code shorter
index <<- 0
new_column = sapply(df$h_no, function(x) {
  if(x == 1) index = index + 1
  return(index)
})

Hàm lặp qua các giá trị trong n_hovà luôn trả về phân loại mà giá trị hiện tại thuộc về. Nếu giá trị của 1được phát hiện, chúng tôi tăng biến toàn cục indexvà tiếp tục.


Tôi thích hack với biến toàn cục. Vì vậy, Cish. : P
Roman Luštrik

2

Tôi tin rằng sử dụng "cbind" là cách đơn giản nhất để thêm một cột vào khung dữ liệu trong R. Dưới đây là ví dụ:

    myDf = data.frame(index=seq(1,10,1), Val=seq(1,10,1))
    newCol= seq(2,20,2)
    myDf = cbind(myDf,newCol)

1
Data.frame[,'h_new_column'] <- as.integer(Data.frame[,'h_no'], breaks=c(1, 4, 7))

0

Phương pháp tiếp cận dựa trên việc xác định số lượng nhóm ( xtrong mapply) và độ dài của nó ( ytrong mapply)

mytb<-read.table(text="h_no  h_freq  h_freqsq group
1     0.09091 0.008264628 1
2     0.00000 0.000000000 1
3     0.04545 0.002065702 1
4     0.00000 0.000000000 1  
1     0.13636 0.018594050 2
2     0.00000 0.000000000 2
3     0.00000 0.000000000 2
4     0.04545 0.002065702 2
5     0.31818 0.101238512 2
6     0.00000 0.000000000 2
7     0.50000 0.250000000 2 
1     0.13636 0.018594050 3 
2     0.09091 0.008264628 3
3     0.40909 0.167354628 3
4     0.04545 0.002065702 3", header=T, stringsAsFactors=F)
mytb$group<-NULL

positionsof1s<-grep(1,mytb$h_no)

mytb$newgroup<-unlist(mapply(function(x,y) 
  rep(x,y),                      # repeat x number y times
  x= 1:length(positionsof1s),    # x is 1 to number of nth group = g1:g3
  y= c( diff(positionsof1s),     # y is number of repeats of groups g1 to penultimate (g2) = 4, 7
        nrow(mytb)-              # this line and the following gives number of repeat for last group (g3)
          (positionsof1s[length(positionsof1s )]-1 )  # number of rows - position of penultimate group (g2) 
      ) ) )
mytb
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.