Cách định hình lại dữ liệu từ định dạng dài sang rộng


263

Tôi gặp sự cố khi sắp xếp lại khung dữ liệu sau:

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

Tôi muốn định hình lại nó sao cho mỗi biến "tên" duy nhất là một danh hiệu, với "giá trị" là các quan sát dọc theo hàng đó và "số" là tên màu. Sắp xếp như thế này:

     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

Tôi đã xem xét meltcastvà một vài thứ khác, nhưng không ai dường như thực hiện công việc.



4
@Frank: đây là một tiêu đề tốt hơn nhiều. dài hình thứcrộng hình thức là các thuật ngữ tiêu chuẩn được sử dụng. Câu trả lời khác không thể được tìm thấy bằng cách tìm kiếm trên các điều khoản.
smci

thêm một câu hỏi: làm thế nào để thay đổi nó trở lại?
HappyLiang

Câu trả lời:


255

Sử dụng reshapechức năng:

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

13
+1 và bạn không cần phải dựa vào các gói bên ngoài, kể từ khi reshapeđi kèm stats. Chưa kể rằng nó nhanh hơn! =)
aL3xa

@indra_patil - Tôi có thể sẽ sử dụng gói reshape2 như được chỉ ra trong một trong những câu trả lời khác. Bạn có thể tạo một câu hỏi mới dành riêng cho trường hợp sử dụng của bạn và đăng nó nếu bạn không thể tìm ra câu hỏi.
Đuổi theo

5
reshapelà một ví dụ nổi bật cho API chức năng khủng khiếp. Nó rất gần với vô dụng.
NoBackingDown

14
Các reshapeý kiến ​​và tên đối số tương tự không phải là hữu ích. Tuy nhiên, tôi đã thấy rằng từ lâu đến rộng, bạn cần cung cấp data =data.frame, idvar= biến xác định các nhóm của bạn, v.names= các biến sẽ trở thành nhiều cột ở định dạng rộng, timevar= biến chứa các giá trị sẽ được thêm vào để v.namesở định dạng rộng, direction = widesep = "_". Rõ ràng đủ chưa? ;)
Brian D

3
Tôi muốn nói rằng cơ sở R vẫn giành chiến thắng trong việc bỏ phiếu theo hệ số khoảng 2 đến 1
vonjd

129

Gói mới (năm 2014) tidyrcũng thực hiện điều này một cách đơn giản, với gather()/ spread()là các điều khoản cho melt/ cast.

Chỉnh sửa: Bây giờ, vào năm 2019, tidyr v 1.0 vừa ra mắt và bộ spreadgathertrên một con đường không dùng nữa, thay vào đó thích pivot_widerpivot_longer, mà bạn có thể tìm thấy mô tả trong câu trả lời này . Đọc tiếp nếu bạn muốn một cái nhìn thoáng qua về cuộc sống ngắn ngủi của spread/gather.

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

Từ github ,

tidyrlà một reshape2sự sắp xếp lại được thiết kế để đi kèm với khung dữ liệu gọn gàng và để bắt tay với magrittrdplyrxây dựng một đường ống vững chắc để phân tích dữ liệu.

Cũng như reshape2đã làm ít hơn định hình lại, tidyrlàm ít hơn reshape2. Nó được thiết kế đặc biệt để thu thập dữ liệu, không phải là định hình lại chung reshape2, hay tổng hợp chung mà định hình lại. Cụ thể, các phương thức tích hợp chỉ hoạt động cho các khung dữ liệu và tidyrkhông cung cấp lề hoặc tổng hợp.


5
Chỉ muốn thêm một liên kết đến trang R Cookbook thảo luận về việc sử dụng các chức năng này từ tidyrreshape2. Nó cung cấp các ví dụ tốt và giải thích.
Jake

71

Bạn có thể làm điều này với reshape()chức năng hoặc với melt()/ các cast()chức năng trong gói định hình lại. Đối với tùy chọn thứ hai, mã ví dụ là

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

Hoặc sử dụng reshape2

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

2
Có thể đáng lưu ý rằng chỉ sử dụng casthoặc dcastsẽ không hoạt động tốt nếu bạn không có cột "giá trị" rõ ràng. Hãy thử 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)và bạn sẽ không nhận được những gì bạn mong đợi. Bạn cần lưu ý rõ ràng value/value.var- cast(dat, id ~ index, value="blah")dcast(dat, id ~ index, value.var="blah")ví dụ.
thelHRail

45

Một tùy chọn khác nếu hiệu suất là mối quan tâm là sử dụng data.tablephần mở rộng của reshape2các hàm tan & dcast

( Tham khảo: Định hình lại hiệu quả bằng cách sử dụng 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

Và, kể từ data.table v1.9.6, chúng ta có thể truyền trên nhiều cột

## 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.tableCách tiếp cận là tốt nhất! rất hiệu quả ... bạn sẽ thấy sự khác biệt khi namekết hợp 30 - 40 cột !!
joel.wilson

Nếu tôi muốn lấy tối đa thì sao?
T.Fung

@ T.Fung Tôi không hiểu bạn đang hỏi gì. Có thể là tốt nhất để mở một câu hỏi mới?
SymbolixAU

@SymbolixAU trong câu hỏi 'tên' và 'số' của op là những kết hợp độc đáo. Điều gì xảy ra nếu họ không và tôi muốn lấy giá trị tối đa cho mỗi kết hợp sau khi xoay vòng? Không phải là một vấn đề nếu quá khó một câu hỏi. Chỉ là thức ăn cho những suy nghĩ. Cảm ơn bạn.
T.Fung

Câu trả lời chính xác. Cảm ơn bạn. Đối với nhiều cột, tôi đã nhận được "Lỗi trong .subset2 (x, i, chính xác = chính xác)" và có thể khắc phục điều này bằng cách buộc sử dụng data.table dcast: xem stackoverflow.com/a/44271092/190791
Timothée HENRY

26

Sử dụng khung dữ liệu mẫu của bạn, chúng tôi có thể:

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

2
cái này là tốt, nhưng kết quả là bảng định dạng có thể không dễ xử lý như data.frame hoặc data.table, cả hai đều có rất nhiều gói
cloudscomputes

18

Hai tùy chọn khác:

Gói cơ sở:

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

sqldf gói:

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
Thay vì số mã hóa cứng, truy vấn có thể được thiết lập như sau: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

Sử dụng aggregatechức năng cơ sở 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

Với phiên bản phát âm của tidyr ‘0.8.3.9000’, có pivot_widerpivot_longerđược khái quát hóa để thực hiện việc định hình lại (dài -> rộng, rộng -> dài, tương ứng) từ 1 đến nhiều cột. Sử dụng dữ liệu của OP

-single cột dài -> rộng

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

-> đã tạo một cột khác để hiển thị chức năng

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

Hàm cơ sở reshapehoạt động hoàn toàn tốt:

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

Ở đâu

  • idvar là cột của các lớp ngăn cách các hàng
  • timevar là cột của các lớp để đúc rộng
  • v.names là cột chứa các giá trị số
  • direction chỉ định định dạng rộng hoặc dài
  • sepđối số tùy chọn là dấu phân cách được sử dụng ở giữa timevartên lớp và v.namestrong đầu ra data.frame.

Nếu không idvartồn tại, hãy tạo một cái trước khi sử dụng reshape()hàm:

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

Chỉ cần nhớ rằng idvarlà bắt buộc! Các timevarv.namesmột phần là dễ dàng. Đầu ra của chức năng này dễ dự đoán hơn một số chức năng khác, vì mọi thứ đều được xác định rõ ràng.


7

Có rất mạnh mẽ gói mới từ các nhà khoa học thiên tài liệu tại Win-Vector (folks làm vtreat, seplyrreplyr) được gọi cdata. Nó thực hiện các nguyên tắc "dữ liệu phối hợp" được mô tả trong tài liệu này và cả trong bài đăng trên blog này . Ý tưởng là cho dù bạn tổ chức dữ liệu của mình như thế nào, thì cũng có thể xác định các điểm dữ liệu riêng lẻ bằng cách sử dụng một hệ thống "tọa độ dữ liệu". Đây là một đoạn trích từ bài đăng trên blog gần đây của John Mount:

Toàn bộ hệ thống dựa trên hai nguyên hàm hoặc toán tử cdata :: moveValuesToRowsD () và cdata :: moveValuesToColumnsD (). Các toán tử này có trục, không trục, mã hóa một nóng, chuyển vị, di chuyển nhiều hàng và cột và nhiều biến đổi khác như các trường hợp đặc biệt đơn giản.

Thật dễ dàng để viết nhiều hoạt động khác nhau về mặt nguyên thủy cdata. Các toán tử này có thể làm việc trong bộ nhớ hoặc ở quy mô dữ liệu lớn (với cơ sở dữ liệu và Apache Spark; đối với dữ liệu lớn, hãy sử dụng các biến thể cdata :: moveValuesToRowsN () và cdata :: moveValuesToColumnsN ()). Các biến đổi được điều khiển bởi một bảng điều khiển mà chính nó là sơ đồ của (hoặc hình ảnh) của biến đổi.

Trước tiên chúng tôi sẽ xây dựng bảng điều khiển (xem bài đăng trên blog để biết chi tiết) và sau đó thực hiện việc di chuyển dữ liệu từ hàng sang cột.

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

cách dễ dàng hơn nhiều!

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

nếu bạn muốn quay lại từ rộng thành dài, chỉ thay đổi Rộng thành Dài và không thay đổi đối tượng.

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