Bagaimana cara membentuk kembali data dari format panjang ke lebar


263

Saya mengalami kesulitan mengatur ulang bingkai data berikut:

set.seed(45)
dat1 <- data.frame(
    name = rep(c("firstName", "secondName"), each=4),
    numbers = rep(1:4, 2),
    value = rnorm(8)
    )

dat1
       name  numbers      value
1  firstName       1  0.3407997
2  firstName       2 -0.7033403
3  firstName       3 -0.3795377
4  firstName       4 -0.7460474
5 secondName       1 -0.8981073
6 secondName       2 -0.3347941
7 secondName       3 -0.5013782
8 secondName       4 -0.1745357

Saya ingin membentuknya kembali sehingga setiap variabel "nama" yang unik adalah nama belakang, dengan "nilai" sebagai pengamatan di sepanjang baris itu dan "angka" sebagai colnames. Semacam seperti ini:

     name          1          2          3         4
1  firstName  0.3407997 -0.7033403 -0.3795377 -0.7460474
5 secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357

Saya telah melihat meltdan castdan beberapa hal lainnya, tetapi tampaknya tidak ada yang melakukan pekerjaan.



4
@ Frank: ini adalah judul yang jauh lebih baik. bentuk panjang dan bentuk lebar adalah istilah standar yang digunakan. Jawaban lain tidak dapat ditemukan dengan mencari pada istilah-istilah itu.
smci

satu pertanyaan lagi: bagaimana cara mengubahnya kembali?
HappyLiang

Jawaban:


257

Menggunakan reshapefungsi:

reshape(dat1, idvar = "name", timevar = "numbers", direction = "wide")

13
+1 dan Anda tidak perlu bergantung pada paket eksternal, karena sudah reshapedisertakan stats. Belum lagi lebih cepat! =)
aL3xa

@indra_patil - Saya kemungkinan akan menggunakan paket reshape2 seperti yang ditunjukkan dalam salah satu jawaban lainnya. Anda dapat membuat pertanyaan baru yang khusus untuk use case Anda dan mempostingnya jika Anda tidak bisa mengetahuinya.
Mengejar

5
reshapeadalah contoh luar biasa untuk API fungsi yang mengerikan. Itu sangat dekat dengan tidak berguna.
NoBackingDown

14
The reshapekomentar dan nama-nama argumen yang sama tidak semua yang membantu. Namun, saya telah menemukan bahwa untuk panjang ke lebar, Anda perlu memberikan data data =Anda.frame, idvar= variabel yang mengidentifikasi grup Anda, v.names= variabel yang akan menjadi beberapa kolom dalam format lebar, timevar= variabel yang berisi nilai-nilai yang akan ditambahkan untuk v.namesdi format lebar, direction = widedan sep = "_". Cukup jelas? ;)
Brian D

3
Saya akan mengatakan pangkalan R masih menang secara bijaksana dengan faktor sekitar 2
banding

129

Paket baru (tahun 2014) tidyrjuga melakukan ini secara sederhana, dengan gather()/ spread()menjadi syarat untuk melt/ cast.

Sunting: Sekarang, pada tahun 2019, tidyr v 1.0 telah diluncurkan dan ditetapkan spreaddan gatherdi jalur penghentian, lebih memilih pivot_widerdan pivot_longer, yang dapat Anda temukan dijelaskan dalam jawaban ini . Baca terus jika Anda ingin melihat sekilas ke dalam kehidupan singkat spread/gather.

library(tidyr)
spread(dat1, key = numbers, value = value)

Dari github ,

tidyradalah pembingkaian ulang yang reshape2dirancang untuk menyertai kerangka kerja data yang rapi, dan untuk bekerja bahu-membahu dengan magrittrdan dplyrmembangun jalur pipa yang solid untuk analisis data.

Sama seperti reshape2halnya kurang dari membentuk kembali, tidyrjuga kurang dari reshape2. Ini dirancang khusus untuk merapikan data, bukan pembentukan kembali umum yang reshape2melakukannya, atau agregasi umum yang membentuk kembali lakukan. Secara khusus, metode bawaan hanya berfungsi untuk bingkai data, dan tidyrtidak memberikan margin atau agregasi.


5
Hanya ingin menambahkan tautan ke halaman R Cookbook yang membahas penggunaan fungsi-fungsi ini dari tidyrdan reshape2. Ini memberikan contoh dan penjelasan yang baik.
Jake

71

Anda dapat melakukan ini dengan reshape()fungsi, atau dengan melt()/ cast()fungsi dalam paket membentuk kembali. Untuk opsi kedua, contoh kode adalah

library(reshape)
cast(dat1, name ~ numbers)

Atau menggunakan reshape2

library(reshape2)
dcast(dat1, name ~ numbers)

2
Mungkin perlu dicatat bahwa hanya menggunakan castatau dcasttidak akan berfungsi dengan baik jika Anda tidak memiliki kolom "nilai" yang jelas. Coba dat <- data.frame(id=c(1,1,2,2),blah=c(8,4,7,6),index=c(1,2,1,2)); dcast(dat, id ~ index); cast(dat, id ~ index)dan Anda tidak akan mendapatkan apa yang Anda harapkan. Anda perlu secara eksplisit mencatat value/value.var- cast(dat, id ~ index, value="blah")dan dcast(dat, id ~ index, value.var="blah")misalnya.
thelatemail

45

Opsi lain jika kinerja menjadi perhatian adalah menggunakan data.tableekstensi reshape2fungsi lebur & saluran

( Referensi: Pembentukan ulang secara efisien menggunakan data.tables )

library(data.table)

setDT(dat1)
dcast(dat1, name ~ numbers, value.var = "value")

#          name          1          2         3         4
# 1:  firstName  0.1836433 -0.8356286 1.5952808 0.3295078
# 2: secondName -0.8204684  0.4874291 0.7383247 0.5757814

Dan, pada data.table v1.9.6 kita dapat menampilkan beberapa kolom

## add an extra column
dat1[, value2 := value * 2]

## cast multiple value columns
dcast(dat1, name ~ numbers, value.var = c("value", "value2"))

#          name    value_1    value_2   value_3   value_4   value2_1   value2_2 value2_3  value2_4
# 1:  firstName  0.1836433 -0.8356286 1.5952808 0.3295078  0.3672866 -1.6712572 3.190562 0.6590155
# 2: secondName -0.8204684  0.4874291 0.7383247 0.5757814 -1.6409368  0.9748581 1.476649 1.1515627

5
data.tablependekatan adalah yang terbaik! sangat efisien ... Anda akan melihat perbedaannya ketika namekombinasi 30-40 kolom !!
joel.wilson

Bagaimana jika saya ingin mengambil maks?
T.Fung

@ T.ung, aku tidak mengerti apa yang kamu minta. Apakah sebaiknya membuka pertanyaan baru?
SymbolixAU

@SymbolixAU dalam pertanyaan op 'nama' dan 'angka' adalah kombinasi unik. Bagaimana jika tidak dan saya ingin mengambil nilai maks untuk setiap kombinasi setelah berputar? Tidak masalah jika terlalu fiddly pertanyaan. Hanya makanan untuk dipikirkan. Terima kasih.
T.Fung

Jawaban yang bagus Terima kasih. Untuk beberapa kolom, saya mendapat "Kesalahan dalam .subset2 (x, i, tepat = tepat)", dan dapat memperbaikinya dengan memaksa penggunaan data.table dcast: lihat stackoverflow.com/a/44271092/190791
Timothée HENRY

26

Menggunakan contoh dataframe Anda, kami dapat:

xtabs(value ~ name + numbers, data = dat1)

2
ini bagus, tetapi hasilnya adalah tabel format yang tidak mungkin tidak begitu mudah ditangani sebagai data.frame atau data.table, keduanya memiliki banyak paket
cloudcomputes

18

Dua opsi lainnya:

Paket dasar:

df <- unstack(dat1, form = value ~ numbers)
rownames(df) <- unique(dat1$name)
df

sqldf paket:

library(sqldf)
sqldf('SELECT name,
      MAX(CASE WHEN numbers = 1 THEN value ELSE NULL END) x1, 
      MAX(CASE WHEN numbers = 2 THEN value ELSE NULL END) x2,
      MAX(CASE WHEN numbers = 3 THEN value ELSE NULL END) x3,
      MAX(CASE WHEN numbers = 4 THEN value ELSE NULL END) x4
      FROM dat1
      GROUP BY name')

1
Alih-alih nomor hardcoding, kueri dapat diatur seperti ini:ValCol <- unique(dat1$numbers);s <- sprintf("MAX(CASE WHEN numbers = %s THEN value ELSE NULL END) `%s`,", ValCol, ValCol);mquerym <- gsub('.{1}$','',paste(s, collapse = "\n"));mquery <- paste("SELECT name,", mquerym, "FROM dat1", "GROUP BY name", sep = "\n");sqldf(mquery)
M--

13

Menggunakan aggregatefungsi dasar R :

aggregate(value ~ name, dat1, I)

# name           value.1  value.2  value.3  value.4
#1 firstName      0.4145  -0.4747   0.0659   -0.5024
#2 secondName    -0.8259   0.1669  -0.8962    0.1681

11

Dengan versi devel tidyr ‘0.8.3.9000’, ada pivot_widerdan pivot_longeryang digeneralisasi untuk melakukan pembentukan kembali (panjang -> lebar, lebar -> panjang, masing-masing) dari 1 ke beberapa kolom. Menggunakan data OP

kolom -sama panjang -> lebar

library(dplyr)
library(tidyr)
dat1 %>% 
    pivot_wider(names_from = numbers, values_from = value)
# A tibble: 2 x 5
#  name          `1`    `2`    `3`    `4`
#  <fct>       <dbl>  <dbl>  <dbl>  <dbl>
#1 firstName   0.341 -0.703 -0.380 -0.746
#2 secondName -0.898 -0.335 -0.501 -0.175

-> membuat kolom lain untuk menunjukkan fungsionalitas

dat1 %>% 
    mutate(value2 = value * 2) %>% 
    pivot_wider(names_from = numbers, values_from = c("value", "value2"))
# A tibble: 2 x 9
#  name       value_1 value_2 value_3 value_4 value2_1 value2_2 value2_3 value2_4
#  <fct>        <dbl>   <dbl>   <dbl>   <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
#1 firstName    0.341  -0.703  -0.380  -0.746    0.682   -1.41    -0.759   -1.49 
#2 secondName  -0.898  -0.335  -0.501  -0.175   -1.80    -0.670   -1.00    -0.349

8

Fungsi dasar reshapeberfungsi dengan baik:

df <- data.frame(
  year   = c(rep(2000, 12), rep(2001, 12)),
  month  = rep(1:12, 2),
  values = rnorm(24)
)
df_wide <- reshape(df, idvar="year", timevar="month", v.names="values", direction="wide", sep="_")
df_wide

Dimana

  • idvar adalah kolom kelas yang memisahkan baris
  • timevar adalah kolom kelas untuk dilempar lebar
  • v.names adalah kolom yang berisi nilai numerik
  • direction menentukan format lebar atau panjang
  • separgumen opsional adalah pemisah yang digunakan di antara timevarnama-nama kelas dan v.namesdalam output data.frame.

Jika tidak idvarada, buat satu sebelum menggunakan reshape()fungsi:

df$id   <- c(rep("year1", 12), rep("year2", 12))
df_wide <- reshape(df, idvar="id", timevar="month", v.names="values", direction="wide", sep="_")
df_wide

Ingat itu idvarwajib! Bagian timevardan v.namesmudah. Output dari fungsi ini lebih dapat diprediksi daripada yang lain, karena semuanya didefinisikan secara eksplisit.


7

Ada paket baru yang sangat kuat dari para ilmuwan data jenius di Win-Vector (orang yang membuat vtreat, seplyrdan replyr) disebut cdata. Ini mengimplementasikan prinsip "data terkoordinasi" yang dijelaskan dalam dokumen ini dan juga dalam posting blog ini . Idenya adalah bahwa terlepas dari bagaimana Anda mengatur data Anda, itu harus mungkin untuk mengidentifikasi titik data individual menggunakan sistem "koordinat data". Berikut kutipan dari posting blog terbaru oleh John Mount:

Seluruh sistem didasarkan pada dua primitif atau operator cdata :: moveValuesToRowsD () dan cdata :: moveValuesToColumnsD (). Operator-operator ini memiliki pivot, un-pivot, one-hot encode, transpos, memindahkan banyak baris dan kolom, dan banyak transformasi lainnya sebagai case khusus sederhana.

Sangat mudah untuk menulis banyak operasi berbeda dalam hal primitif cdata. Operator-operator ini dapat bekerja di memori atau pada skala data besar (dengan basis data dan Apache Spark; untuk data besar menggunakan cdata :: moveValuesToRowsN () dan varian cdata :: moveValuesToColumnsN ()). Transformasi dikendalikan oleh tabel kontrol yang dengan sendirinya merupakan diagram (atau gambar) transformasi.

Kami pertama-tama akan membangun tabel kontrol (lihat posting blog untuk detail) dan kemudian melakukan pemindahan data dari baris ke kolom.

library(cdata)
# first build the control table
pivotControlTable <- buildPivotControlTableD(table = dat1, # reference to dataset
                        columnToTakeKeysFrom = 'numbers', # this will become column headers
                        columnToTakeValuesFrom = 'value', # this contains data
                        sep="_")                          # optional for making column names

# perform the move of data to columns
dat_wide <- moveValuesToColumnsD(tallTable =  dat1, # reference to dataset
                    keyColumns = c('name'),         # this(these) column(s) should stay untouched 
                    controlTable = pivotControlTable# control table above
                    ) 
dat_wide

#>         name  numbers_1  numbers_2  numbers_3  numbers_4
#> 1  firstName  0.3407997 -0.7033403 -0.3795377 -0.7460474
#> 2 secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357

1

cara yang jauh lebih mudah!

devtools::install_github("yikeshu0611/onetree") #install onetree package

library(onetree)
widedata=reshape_toWide(data = dat1,id = "name",j = "numbers",value.var.prefix = "value")
widedata

        name     value1     value2     value3     value4
   firstName  0.3407997 -0.7033403 -0.3795377 -0.7460474
  secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357

jika Anda ingin kembali dari lebar ke panjang, hanya ubah Lebar ke Panjang, dan tidak ada perubahan pada objek.

reshape_toLong(data = widedata,id = "name",j = "numbers",value.var.prefix = "value")

        name numbers      value
   firstName       1  0.3407997
  secondName       1 -0.8981073
   firstName       2 -0.7033403
  secondName       2 -0.3347941
   firstName       3 -0.3795377
  secondName       3 -0.5013782
   firstName       4 -0.7460474
  secondName       4 -0.1745357
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.