2015-02-17 15 views
5

EDIT: Khi tạo một data.frame mẫu đơn giản, tôi đã sử dụng cùng một ngày cho hai cột Ngày nhưng đây không phải là trường hợp, điều này làm cho vấn đề này khó khăn hơn.R Hiệu suất sắp xếp lại

Thay vì dataframe này:

ID  Date   Balance Date2  Balance2 
1  01-01-2014  10000  01-02-2014 5000 
2  01-01-2014  50000  01-02-2014 30000 
3  01-01-2014  30000  01-02-2014 15000 
4  01-01-2014  5000  01-02-2014 3500 

Tôi có dataframe này thay vì:

ID  Date   Balance Date2  Balance2 
1  01-01-2014  10000  01-02-2017 5000 
2  01-01-2015  50000  01-02-2016 30000 
3  01-08-2014  30000  01-02-2015 15000 
4  01-02-2016  5000  01-02-2018 3500 

Mà tôi muốn định hình lại như sau:

ID  Date   Balance 
1  01-01-2014  10000  
1  02-02-2017  5000 
2  01-01-2015  50000  
2  01-02-2016  30000  
3  ...   ...  And so on... 

Tôi có sau tại thời điểm này.

Dates = a character containing all the columns with Dates (Date, Date2, Date3...) 
Balances = a character containing all the columns with Balances (Balance1, Balance2...) 

df <- reshape(df, 
       varying = Balances, 
       v.names = "Balance" 
       timevar = "Date" 
       times = Dates, 
       direction = "long") 

Kết quả với các phương pháp được đề xuất xuất sắc của bạn không mang lại kết quả khi tôi thay đổi dữ liệu mẫu.frame/data.table.

Vấn đề chính là tôi có các ngày khác nhau trong cột ngày tháng, không có cách nào tôi có thể thay đổi điều này. Date1 - Date2 - Date3 luôn theo thứ tự thời gian.

Tôi cần một cách mà R hiểu rằng cần phải lấy cột Ngày và cột Số dư, đặt nó trong một DF mới, sau đó lấy Date2 và Balance2, rbind chúng với DF đầu tiên, sau đó Date3, Balance3 và như vậy , cho đến khi tôi nhận được 700 biến số của mình.

Tôi đang nghĩ đến việc viết một vòng lặp, bất kỳ suy nghĩ nào? Xem bên dưới để biết dữ liệu mẫu.

Cảm ơn trước,

Robert

df <- data.frame(ID=seq(1:4), 
       Date= c("01-01-2014","01-01-2015","01-08-2014","01-02-2016"), 
       Balance = c(10000,50000,30000,5000), 
       Date2= c("01-02-2017","01-02-2016","01-02-2015","01-02-2018"), 
      Balance2 = c(5000,30000,15000,3500)) 
+0

tôi có thể 't bình luận về vấn đề cụ thể của bạn nhưng tôi thúc giục bạn chuyển sang 'reshape2', đi kèm với một API được cải thiện rất nhiều, và được thực hiện trong C++ đi kèm với những cải tiến hiệu năng tiềm năng đáng kể. –

+0

@RobertLuyt, xem chỉnh sửa của tôi. Hãy cho tôi biết nếu nó làm việc cho bạn. – bgoldst

Trả lời

2

Không phải là giải pháp đơn giản nhất để xây dựng một data.frame mới bằng cách kết hợp hai bộ cột? Điều này có thể được thực hiện mà không cần reshape:

r> x <- data.frame(ID=1:4, Date=as.POSIXct(c('2014-01-01','2014-01-01','2014-01-01','2014-01-01')), Balance=c(10000,50000,30000,5000), Date2=as.POSIXct(c('2014-01-02','2014-01-02','2014-01-02','2014-01-02')), Balance2=c(5000,30000,15000,3500)); 
r> y <- data.frame(ID=c(x$ID,x$ID), Date=c(x$Date,x$Date2), Balance=c(x$Balance,x$Balance2)); 
r> y; 
    ID  Date Balance 
1 1 2014-01-01 10000 
2 2 2014-01-01 50000 
3 3 2014-01-01 30000 
4 4 2014-01-01 5000 
5 1 2014-01-02 5000 
6 2 2014-01-02 30000 
7 3 2014-01-02 15000 
8 4 2014-01-02 3500 

Bạn có thể cho tôi biết nếu điều này hoạt động tốt cho dữ liệu của bạn không?

Đối với sắp xếp:

r> z <- y[order(y$ID,y$Date),]; rownames(z) <- 1:nrow(z); 
r> z; 
    ID  Date Balance 
1 1 2014-01-01 10000 
2 1 2014-01-02 5000 
3 2 2014-01-01 50000 
4 2 2014-01-02 30000 
5 3 2014-01-01 30000 
6 3 2014-01-02 15000 
7 4 2014-01-01 5000 
8 4 2014-01-02 3500 

Edit: Xét bạn có rất nhiều cột, tay gọi c() trên mỗi ngày tương ứng và Cân bằng cột là không thực tế. Tuy nhiên, sau khi chơi một chút, tôi nhận ra rằng bạn có thể kết hợp các hàm names(), grep(), do.call()c() để tự động trích xuất và kết hợp dữ liệu của bạn theo cách bạn muốn. Bạn cũng sẽ cần unname() để xóa các tên phần tử không mong muốn và replicate() để nhân rộng cột ID một số lần đủ.

Trước tiên, tôi đã tìm ra cách tạo dữ liệu đầu vào ngẫu nhiên.khung để thử nghiệm:

r> randDate <- function() as.Date('2014-01-01')+as.integer(runif(1,max=30)); 
r> randBalance <- function() 5000+as.integer(runif(1,max=18))*5000; 
r> n <- 700; 
r> x <- setNames(do.call(data.frame, c(list(1:4), replicate(n, list(do.call(c, replicate(4, randDate(), simplify=F)), do.call(c, replicate(4, randBalance(), simplify=F))), simplify=F))), c('ID', sapply(1:n, function(x) c(paste0('Date',x), paste0('Balance',x))))); 
r> x; 
    ID  Date1 Balance1  Date2 Balance2  Date3 Balance3 ... Balance698 Date699 Balance699 Date700 Balance700 
1 1 2014-01-29 10000 2014-01-08 50000 2014-01-05 40000 ...  30000 2014-01-23  35000 2014-01-08  45000 
2 2 2014-01-30 65000 2014-01-15 10000 2014-01-11 45000 ...  75000 2014-01-29  25000 2014-01-04  50000 
3 3 2014-01-11 75000 2014-01-14 70000 2014-01-24 45000 ...  50000 2014-01-02  10000 2014-01-01  50000 
4 4 2014-01-11 25000 2014-01-11 20000 2014-01-24 20000 ...  50000 2014-01-08  70000 2014-01-11  75000 

Bây giờ, bạn có thể đạt được định hình lại mong muốn bằng cách sử dụng sau đây:

r> y <- data.frame(ID=do.call(c, replicate((ncol(x)-1)/2, x$ID, simplify=F)), Date=unname(do.call(c, x[,grep('^Date[0-9]+$', names(x))])), Balance=unname(do.call(c, x[,grep('^Balance[0-9]+$', names(x))]))); 
r> y; 
    ID  Date Balance 
1  1 2014-01-29 10000 
2  2 2014-01-30 65000 
3  3 2014-01-11 75000 
4  4 2014-01-11 25000 
5  1 2014-01-08 50000 
6  2 2014-01-15 10000 
... 
2795 3 2014-01-02 10000 
2796 4 2014-01-08 70000 
2797 1 2014-01-08 45000 
2798 2 2014-01-04 50000 
2799 3 2014-01-01 50000 
2800 4 2014-01-11 75000 

Và đối với trật tự:

r> z <- y[order(y$ID,y$Date),]; rownames(z) <- 1:nrow(z); 
r> z; 
    ID  Date Balance 
1  1 2014-01-01 55000 
2  1 2014-01-01 20000 
3  1 2014-01-01 15000 
4  1 2014-01-01 75000 
5  1 2014-01-01 40000 
6  1 2014-01-01 85000 
... 
2795 4 2014-01-30 15000 
2796 4 2014-01-30 65000 
2797 4 2014-01-30 5000 
2798 4 2014-01-30 70000 
2799 4 2014-01-30 35000 
2800 4 2014-01-30 30000 

Mã này chạy về cơ bản ngay lập tức. Chìa khóa cho tốc độ là nó trích xuất từng cột đầu vào cho một cột đầu ra mục tiêu cùng lúc bằng cách subscripting data.frame (ví dụ: x[,grep('^Date[0-9]+$', names(x))] cho tất cả các cột Date) và chạy tất cả chúng thông qua một cuộc gọi đến c() qua một cuộc gọi đến do.call, bỏ qua lớp data.frame của đối số và chỉ coi nó là danh sách cơ bản. Kết quả cuối cùng là bạn nhận được một vector c()-kết hợp của cột đầu ra theo một biểu mẫu là gần như sẵn sàng để đính kèm vào tệp dữ liệu đầu ra (bạn chỉ cần xóa các tên phần tử không mong muốn bằng cách sử dụng unname()). Bạn cần thực hiện việc này cho cột Ngày và Cột cân bằng một cách độc lập (cột Số dư được lập chỉ mục qua x[,grep('^Balance[0-9]+$', names(x))]) và gộp chúng lại với nhau trong một cuộc gọi xây dựng data.frame mới. Phần khác của câu đố là sao chép cột ID đầu vào đủ số lần ((ncol(x)-1)/2) để tạo cột ID đầu ra chính xác tương ứng với vectơ đầu ra ngày và số dư.

Giải pháp này hoàn toàn được vector hóa, không có vòng lặp rõ ràng hoặc ẩn. Ngoài ra, nó chỉ sử dụng chức năng R tích hợp; nó không yêu cầu sự phụ thuộc vào bất kỳ gói bổ sung nào. Tôi luôn cố gắng tránh sử dụng các gói phần mềm bổ sung, có xu hướng thêm những khó khăn phức tạp và bảo trì sau này, vì chiều rộng kiến ​​thức cần thiết để hiểu được mã tăng lên.

+0

Và sau đó bạn nhận được những gì người được hỏi yêu cầu bạn có thể đặt hàng bằng ID. Thứ tự đó có thể hơi chậm. – John

+0

hoặc 'rbind (DF [, 1: 3], DF [, c (1, 4: 5)])' – Roland

+0

@Roland, tôi thích đường viền đề xuất của bạn, nhưng khi tôi chạy nó tôi nhận được 'Lỗi trong trận đấu .names (clabs, names (xi)): tên không khớp với các tên trước đó' và 'rbind()' dường như không cung cấp bất kỳ cách nào để chỉ định tên. Có cách nào để làm cho 'rbind()' hoạt động không? – bgoldst

2

Nếu bạn quan tâm đến trật tự, có lẽ phương pháp nhanh nhất sẽ đến từ data.table câu trả lời. Nhưng nếu không thì bạn chỉ có thể kết buộc các hàng của ba cột đầu tiên với hai cột đầu tiên và cuối cùng bằng cách sử dụng rbind. Điều đó sẽ rất nhanh và đơn giản nhưng không có thứ tự bạn mong muốn. Bạn có thể sắp xếp lại với chức năng order trên ID.

Hoặc bạn có thể tạo hai ma trận, chuyển đổi, và sau đó liên kết tất cả lại với nhau dưới dạng vectơ. Điều này sẽ khá nhanh vì bạn chỉ cần tạo một vài bản sao và lựa chọn và việc sắp xếp lại được thực hiện thông qua việc chỉ xác định dữ liệu theo một cách khác hơn là dựa vào thuật toán sắp xếp.

dateMat <- as.matrix(df[, c(2, 4)]) 
balMat <- as.matrix(df[, c(3, 5)]) 
dates <- as.vector(t(dateMat)) 
balances <- as.vector(t(balMat)) 
dfl <- data.frame(ID = rep(df$ID, each = 2), Date = dates, Balance = balances) 

Bạn có thể kiểm tra hai phiên bản để biết tốc độ trên data.frame lớn.

2

Tùy chọn khác có thể như sau. Sử dụng select trong dplyr, bạn có thể chọn cột và thay đổi tên cột cùng một lúc. Bạn kết hợp hai tập hợp dữ liệu với bind_rows cuối cùng.

DỮ LIỆU & MÃ

mydf <- structure(list(ID = 1:4, Date = structure(c(1L, 1L, 1L, 1L), .Label = "01-01-2014", class = "factor"), 
Balance = c(10000L, 50000L, 30000L, 5000L), Date2 = structure(c(1L, 
1L, 1L, 1L), .Label = "01-02-2014", class = "factor"), Balance2 = c(5000L, 
30000L, 15000L, 3500L)), .Names = c("ID", "Date", "Balance", 
"Date2", "Balance2"), class = "data.frame", row.names = c(NA, 
-4L)) 

# Convert factor to date object 
mutate_each(mydf, funs(as.Date(., format = "%m-%d-%Y")), Date, Date2) -> mydf 

bind_rows(select(mydf, 1:3), select(mydf, 1, Date = Date2, Balance = Balance2)) %>% 
arrange(ID, Date) 

# ID  Date Balance 
#1 1 2014-01-01 10000 
#2 1 2014-01-02 5000 
#3 2 2014-01-01 50000 
#4 2 2014-01-02 30000 
#5 3 2014-01-01 30000 
#6 3 2014-01-02 15000 
#7 4 2014-01-01 5000 
#8 4 2014-01-02 3500 
+0

Tôi sẽ giữ cho bạn cập nhật, điều này có vẻ đầy hứa hẹn. –

+0

@RobertLuyt Bạn có tất cả các loại phương pháp tuyệt vời ở đây. Hãy thử tất cả và xem những gì làm việc tốt nhất cho bạn. :) – jazzurro

3

Nếu các cột được đặt tên như bạn đã cung cấp trong ví dụ của bạn, bạn có thể thử merged.stack từ tôi gói "splitstackshape". Lưu ý rằng các giá trị trong cột "ID" của bạn phải là duy nhất để hoạt động chính xác (vì chúng nằm trong dữ liệu mẫu của bạn).

Cách sử dụng rất đơn giản: Chỉ định "sơ khai" của các biến (tại đây, "Ngày" và "Số dư"). Đặt sep = "var.stubs" chỉ xóa phần còn lại của tên cột.[, .time_1 := NULL] chỉ để thả cột thời gian đã được tạo trong quá trình định hình lại.

library(splitstackshape) 
merged.stack(mydf, var.stubs = c("Date", "Balance"), 
      sep = "var.stubs")[, .time_1 := NULL][] 
# ID  Date Balance 
# 1: 1 01-01-2014 10000 
# 2: 1 01-02-2014 5000 
# 3: 2 01-01-2014 50000 
# 4: 2 01-02-2014 30000 
# 5: 3 01-01-2014 30000 
# 6: 3 01-02-2014 15000 
# 7: 4 01-01-2014 5000 
# 8: 4 01-02-2014 3500 

Ngay (phiên bản 1.9.8 của "data.table") melt sẽ có thể xử lý chuyển đổi sang một hình thức bán lâu như bạn đang cố gắng để có được ở đây. Điều đó sẽ nhanh hơn merged.stack hiện tại, nhưng merged.stack đã có thể xử lý trường hợp hiện tại của bạn.

+0

Hi Ananda - Bạn có một số tham chiếu đề cập đến/mô tả rằng việc tăng cường sắp tới của 'data.table :: melt()'? Scratch rằng: [tìm thấy nó] (https://github.com/Rdatatable/data.table/issues/828)! Cảm ơn cho những người đứng đầu lên. –

+0

@ JoshO'Brien, Chỉ nhận được lời bình luận này. Strange .... Vui mừng bạn tìm thấy các tài liệu tham khảo trong khi chờ đợi :-) Có lẽ một chút xấu hổ để nói nó, nhưng tôi vui mừng bởi sự phát triển này :-) – A5C1D2H2I1M1N2O1R2T1

+0

Hah! Điều đó có nghĩa là có ít nhất hai chúng tôi ...;) –

2

Đây là giải pháp data.table. Tôi vẫn đang cố gắng nghĩ cách xóa các cuộc gọi đến data.table đầu tiên.

dt <- structure(list(ID = 1:4, Date = structure(c(1L, 1L, 1L, 1L), .Label = "01-01-2014", class = "factor"), 
Balance = c(10000L, 50000L, 30000L, 5000L), Date2 = structure(c(1L, 
1L, 1L, 1L), .Label = "01-02-2014", class = "factor"), Balance2 = c(5000L, 
30000L, 15000L, 3500L)), .Names = c("ID", "Date", "Balance", 
"Date2", "Balance2"), class = "data.table", row.names = c(NA, 
-4L)) 

dt1 <- melt(dt,id="ID",measure=c("Balance","Balance2"))[, variable := c(as.character(dt$Date), as.character(dt$Date2))] 
dt1 
1

Như đã đề cập in this answer, phiên bản 1.9.6 của data.table (trên cran 19 tháng 9 năm 2015) đã giới thiệu khả năng để làm tan chảy thành nhiều cột:

library(data.table) 
melt(setDT(df), measure.vars = patterns("Date", "Balance"), 
    value.name = c("Date", "Balance")) 
ID variable  Date Balance 
1: 1  1 01-01-2014 10000 
2: 2  1 01-01-2015 50000 
3: 3  1 01-08-2014 30000 
4: 4  1 01-02-2016 5000 
5: 1  2 01-02-2017 5000 
6: 2  2 01-02-2016 30000 
7: 3  2 01-02-2015 15000 
8: 4  2 01-02-2018 3500 
Các vấn đề liên quan