2017-09-27 14 views
5

Trong tập dữ liệu làm việc của tôi, tôi đang cố gắng tính giá trị theo tuần cho các thay đổi về bán buôn và doanh thu. Mã này dường như hoạt động, nhưng ước tính của tôi cho thấy sẽ mất khoảng 75 giờ để chạy một phép tính dường như đơn giản. Dưới đây là phiên bản tái sản xuất generic mất khoảng 2m để chạy trên tập dữ liệu nhỏ này:Tạo tính tuần qua hiệu quả theo tuần với số dư

######################################################################################################################## 
# MAKE A GENERIC REPORDUCIBLE STACK OVERFLOW QUESTION 
######################################################################################################################## 

# Create empty data frame of 26,000 observations similar to my data, but populated with noise 
exampleData <- data.frame(product = rep(LETTERS,1000), 
          wholesale = rnorm(1000*26), 
          revenue = rnorm(1000*26)) 

# create a week_ending column which increases by one week with every set of 26 "products" 
for(i in 1:nrow(exampleData)){ 
    exampleData$week_ending[i] <- as.Date("2016-09-04")+7*floor((i-1)/26) 
} 
exampleData$week_ending <- as.Date(exampleData$week_ending, origin = "1970-01-01") 

# create empty columns to fill 
exampleData$wholesale_wow <- NA 
exampleData$revenue_wow <- NA 

# loop through the wholesale and revenue numbers and append the week-over-week changes 
for(i in 1:nrow(exampleData)){ 
    # set a condition where the loop only appends the week-over-week values if it's not the first week 
    if(exampleData$week_ending[i]!="2016-09-04"){ 
    # set temporary values for the current and past week's wholesale value 
    currentWholesale <- exampleData$wholesale[i] 
    lastWeekWholesale <- exampleData$wholesale[which(exampleData$product==exampleData$product[i] & 
                 exampleData$week_ending==exampleData$week_ending[i]-7)] 
    exampleData$wholesale_wow[i] <- currentWholesale/lastWeekWholesale -1 

    # set temporary values for the current and past week's revenue 
    currentRevenue <- exampleData$revenue[i] 
    lastWeekRevenue <- exampleData$revenue[which(exampleData$product==exampleData$product[i] & 
                exampleData$week_ending==exampleData$week_ending[i]-7)] 
    exampleData$revenue_wow[i] <- currentRevenue/lastWeekRevenue -1 
    } 
} 

Bất kỳ giúp hiểu tại sao điều này phải mất quá lâu hoặc làm thế nào để cắt giảm thời gian sẽ được nhiều đánh giá cao!

+0

Có lẽ không phải là vấn đề chính nhưng ... không phân tích chuỗi thành các ngày trong vòng lặp; chỉ cần lưu một 'd0 = as.Date (" 2016-09-04 ")' ở đâu đó và sử dụng nó. Ngoài ra không '! =' So với chuỗi phải được phân tích cú pháp cho đến ngày. Tôi nghi ngờ phần chính của mã có thể được viết như là một hợp nhất/tham gia thay vì một vòng lặp. – Frank

+1

Điều này có vẻ tốt và có thể tái sản xuất, nhưng (ít nhất là trong tương lai), tôi cũng khuyên bạn nên tạo ** các ví dụ ** tối thiểu. Sẽ dễ dàng hơn nhiều khi kiểm tra dữ liệu ở từng bước để xem điều gì đang xảy ra với, ví dụ: 2 sản phẩm và 4 tuần, hơn 26 sản phẩm và 140 tuần. – Gregor

+0

Cảm ơn bạn đã phản hồi! Tôi không muốn đơn giản hóa, nhưng bạn nói đúng là nó có thể tối giản hơn. Tôi sẽ ghi nhớ điều này trong lần sau. –

Trả lời

6

Các for vòng đầu tiên có thể được đơn giản hóa như sau cho:

exampleData$week_ending2 <- as.Date("2016-09-04") + 7 * floor((seq_len(nrow(exampleData)) - 1)/26) 

setequal(exampleData$week_ending, exampleData$week_ending2) 
[1] TRUE 

Thay thế thứ hai for loop

library(data.table) 
dt1 <- as.data.table(exampleData) 
dt1[, wholesale_wow := wholesale/shift(wholesale) - 1 , by = product] 
dt1[, revenue_wow := revenue/shift(revenue) - 1 , by = product] 

setequal(exampleData, dt1) 
[1] TRUE 

này mất khoảng 4 mili giây để chạy trên máy tính xách tay của tôi

+0

Cảm ơn, nhưng sự cố thực sự là với vòng lặp thứ hai. Dữ liệu thực của tôi không cần phải có một trường kết thúc vào cuối tuần vì nó đã tồn tại nên tôi vừa tạo ra một ví dụ. –

+0

@WillWright đã thêm mã cho vòng lặp thứ hai thực hiện theo mili giây – manotheshark

+0

Không thể tin được. Tôi không nghĩ rằng tôi đã sử dụng gói data.table trong quá khứ, nhưng tôi chắc chắn sẽ dành chút thời gian để học nó. Giải pháp này hoạt động như một sự quyến rũ! –

1

Đây là một giải pháp vectorized bằng cách sử dụng gói tidyr.

set.seed(123) 
# Create empty data frame of 26,000 observations similar to my data, but populated with noise 
exampleData <- data.frame(product = rep(LETTERS,1000), 
          wholesale = rnorm(1000*26), 
          revenue = rnorm(1000*26)) 

# create a week_ending column which increases by one week with every set of 26 "products" 
#vectorize the creating of the data 
i<-1:nrow(exampleData) 
exampleData$week_ending <- as.Date("2016-09-04")+7*floor((i-1)/26) 

exampleData$week_ending <- as.Date(exampleData$week_ending, origin = "1970-01-01") 

# create empty columns to fill 
exampleData$wholesale_wow <- NA 
exampleData$revenue_wow <- NA 

#find the index of rows of interest (ie removing the first week) 
i<-i[exampleData$week_ending!="2016-09-04"] 

library(tidyr) 

#create temp variables and convert into wide format 
# the rows are product and the columns are the ending weeks 
Wholesale<-exampleData[ ,c(1,2,4)] 
Wholesale<-spread(Wholesale, week_ending, wholesale) 

Revenue<-exampleData[ ,c(1,3,4)] 
Revenue<-spread(Revenue, week_ending, revenue) 

#number of columns 
numCol<-ncol(Wholesale) 

#remove the first two columns for current wholesale 
#remove the first and last column for last week's wholesale 
#perform calculation on ever element in dataframe (divide this week/lastweek) 
Wholesale_wow<- Wholesale[ ,-c(1, 2)]/Wholesale[ ,-c(1, numCol)] - 1 
#convert back to long format 
Wholesale_wow<-gather(Wholesale_wow) 

#repeat for revenue 
Revenue_wow<- Revenue[ ,-c(1, 2)]/Revenue[ ,-c(1, numCol)] - 1 
#convert back to long format 
Revenue_wow<-gather(Revenue_wow) 

#assemble calculated values back into the original dataframe 
exampleData$wholesale_wow[i]<-Wholesale_wow$value 
exampleData$revenue_wow[i]<-Revenue_wow$value 

Chiến lược là chuyển đổi dữ liệu gốc thành định dạng rộng, trong đó các hàng là id sản phẩm và cột là các tuần. Sau đó chia các khung dữ liệu cho nhau. Chuyển đổi thành định dạng dài và thêm các giá trị mới được tính vào khung dữ liệu exampleData. Điều này hoạt động, không phải rất sạch sẽ nhưng nhanh hơn rất nhiều so với vòng lặp. Gói dplyr là một công cụ khác cho loại công việc này.

Để so sánh kết quả này của mã này với bạn kiểm tra sử dụng trường hợp:

print(identical(goldendata, exampleData)) 

đâu goldendata là biết kết quả tốt của bạn, hãy chắc chắn để sử dụng những con số ngẫu nhiên tương tự với set.seed() chức năng.

+0

Tuyệt vời! Tôi biết giải pháp có khả năng sẽ được tái cơ cấu dữ liệu, nhưng tôi vẫn còn mới để suy nghĩ về các giải pháp trong các điều khoản này. Cảm ơn một tấn! Điều này chạy trong vài giây. Tôi chắc chắn sẽ thêm điều này vào toolbelt của tôi. –

Các vấn đề liên quan