Menambahkan kolom ke data.frame

115

Saya memiliki data.frame di bawah ini. Saya ingin menambahkan kolom yang mengklasifikasikan data saya menurut kolom 1 ( h_no) sedemikian rupa sehingga rangkaian pertama h_no 1,2,3,4 adalah kelas 1, rangkaian kedua h_no(1 hingga 7) adalah kelas 2 dll. seperti yang ditunjukkan di kolom terakhir.

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
Susanne Dreisigacker
sumber

Jawaban:

155

Anda dapat menambahkan kolom ke data Anda menggunakan berbagai teknik. Kutipan di bawah ini berasal dari bagian "Detail" teks bantuan yang relevan [[.data.frame,.

Bingkai data dapat diindeks dalam beberapa mode. Ketika [dan [[digunakan dengan indeks vektor tunggal ( x[i]atau x[[i]]), mereka mengindeks bingkai data seolah-olah itu adalah sebuah daftar.

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

Metode data.frame untuk $, diperlakukan xsebagai daftar

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

Ketika [dan [[digunakan dengan dua indeks ( x[i, j]dan x[[i, j]]) mereka bertindak seperti mengindeks matriks

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

Karena metode untuk data.framemengasumsikan bahwa jika Anda tidak menentukan apakah Anda bekerja dengan kolom atau baris, ini akan menganggap yang Anda maksud adalah kolom.


Untuk contoh Anda, ini seharusnya berhasil:

# 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
Roman Luštrik
sumber
Apa perbedaan antara dua metode terakhir menambahkan kolom?
huon
2
@ huon-dbaupp metode dengan koma adalah eksplisit dan juga akan bekerja pada matriks, sedangkan yang terakhir hanya bekerja pada data.frames. Jika tidak ada koma yang diberikan, R menganggap yang Anda maksud adalah kolom.
Roman Luštrik
12

Mudah: Bingkai data Anda adalah A

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

Kemudian Anda mendapatkan kolom b.

pengguna1333396
sumber
Bagus dan pendek. Saya hanya akan mengubah elemen terakhir sehingga cumsum(b) -> bhasilnya akan langsung ditambahkan sebagai kolom ke bingkai data asli, seperti A$groups <- cumsum(b).
A5C1D2H2I1M1N2O1R2T1
cumsum(b)akan memberi Anda vektor dengan panjang 3, atau saya kehilangan sesuatu?
Roman Luštrik
@ RomanLuštrik, lihat solusi dbaupp yang menjelaskan cara kerja cumsum dalam kasus ini.
A5C1D2H2I1M1N2O1R2T1
2
@ RomanLuštrik, Solusi ini dapat ditulis ulang dengan sangat baik dalam satu baris. Menggunakan your.dfdata Anda, Anda dapat melakukannya your.df$group = cumsum(your.df[, 1]==1)untuk mendapatkan kolom grup baru Anda.
A5C1D2H2I1M1N2O1R2T1
7

Jika saya memahami pertanyaan dengan benar, Anda ingin mendeteksi saat h_notidak bertambah dan kemudian menaikkan class. (Saya akan menjelaskan bagaimana saya memecahkan masalah ini, ada fungsi mandiri di bagian akhir.)

Kerja

Kami hanya peduli tentang h_nokolom untuk saat ini, jadi kami dapat mengekstraknya dari bingkai data:

> h_no <- data$h_no

Kami ingin mendeteksi kapan h_notidak naik, yang dapat kami lakukan dengan menghitung ketika perbedaan antara elemen yang berurutan negatif atau nol. R menyediakan difffungsi yang memberi kita vektor perbedaan:

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

Setelah kita mendapatkannya, itu adalah masalah sederhana untuk menemukan yang tidak positif:

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

Di R, TRUEdan FALSEpada dasarnya sama dengan 1dan 0, jadi jika kita mendapatkan jumlah kumulatif dari nonpos, itu akan meningkat 1 di (hampir) tempat yang sesuai. The cumsumfunction (yang pada dasarnya adalah kebalikan dari diff) dapat melakukan hal ini.

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

Tapi, ada dua masalah: angkanya terlalu kecil; dan, kami kehilangan elemen pertama (harus ada empat di kelas pertama).

Masalah pertama adalah hanya diselesaikan: 1+cumsum(nonpos). Dan yang kedua hanya perlu menambahkan a 1ke depan vektor, karena elemen pertama selalu ada di kelas 1:

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

Sekarang, kita dapat melampirkannya kembali ke bingkai data kita dengan cbind(dengan menggunakan class=sintaks, kita dapat memberi classjudul pada kolom ):

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

Dan data_w_classessekarang berisi hasilnya.

Hasil akhir

Kita dapat memampatkan garis menjadi satu dan membungkus semuanya menjadi sebuah fungsi agar lebih mudah digunakan:

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

Atau, karena masuk akal jika classmenjadi faktor:

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

Anda menggunakan salah satu fungsi seperti:

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

(Metode pemecahan masalah ini bagus karena menghindari iterasi eksplisit, yang umumnya direkomendasikan untuk R, dan menghindari menghasilkan banyak vektor dan daftar perantara dll. Dan juga agak rapi bagaimana itu dapat ditulis dalam satu baris :))

huon
sumber
2

Selain jawaban Roman, hal seperti ini mungkin lebih sederhana. Perhatikan bahwa saya belum mengujinya karena saya tidak memiliki akses ke R sekarang.

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

Fungsi iterasi atas nilai di n_hodan selalu mengembalikan kategori yang dimiliki nilai saat ini. Jika nilai 1terdeteksi, kami meningkatkan variabel global indexdan melanjutkan.

Paul Hiemstra
sumber
Saya suka retasan dengan variabel global. Jadi Cish. : P
Roman Luštrik
2

Saya percaya bahwa menggunakan "cbind" adalah cara termudah untuk menambahkan kolom ke bingkai data di R. Di bawah ini contohnya:

    myDf = data.frame(index=seq(1,10,1), Val=seq(1,10,1))
    newCol= seq(2,20,2)
    myDf = cbind(myDf,newCol)
Emanuele Catania
sumber
1
Data.frame[,'h_new_column'] <- as.integer(Data.frame[,'h_no'], breaks=c(1, 4, 7))
pengguna2759975
sumber
0

Pendekatan berdasarkan identifikasi jumlah kelompok ( xdalam mapply) dan panjangnya ( ydalam 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
Ferroao
sumber