2017-08-29 55 views
1

位數我想通過數據分離成位數,相同this great question怎樣計算在團體

的問題是,我要天天在時間序列上做到這一點:

set.seed(123) 
temp.all <- data.frame(date = c(rep(Sys.Date() - 1, 12), rep(Sys.Date(), 12)), 
    name=letters[c(1:12, 1:12)], value=rnorm(24)) 

在此刻,我正在用for循環解決:

library(dplyr) 
for (d in unique(temp.all$date)) { 
    temp = filter(temp.all, date == d) 
    temp$quartile <- with(temp, factor(
         findInterval(val, c(-Inf, 
          quantile(val, probs=c(0.25, .5, .75)), Inf) , na.rm=TRUE), 
         labels=c("Q1","Q2","Q3","Q4") 
)) 
    # ...and doing rbind on 'temp' to reconstruct temp.all with quartiles 
} 

有關如何避免可怕的for-loop的任何想法?有沒有這樣做的group_by方式?

回答

4

使用group_by你可以這樣做:

library(lubridate) 

temp.all = temp.all %>% 
    # lubridate::date(date) might be necessary if you have datetimes 
    group_by(date) %>% 
    mutate(quartile = cut(value, breaks = 4, labels = paste0("Q", 1:4))) 

dplyr還具有功能ntile應該類似的行爲cut,並應給予同樣的結果。

+0

'ntile'函數可以任意打斷關係,所以我會堅持'cut'。我明白你的觀點:使用'group_by'然後'mutate'。謝謝! – lebelinoz

+1

我同意@Uwe,代碼適用於示例數據,但會在一個多月的同一天給出錯誤答案。只是'group_by(date)'應該這樣做。 – neilfws

1

爲了完整起見(和糾正一個錯誤的答案),這裏也是一個data.table解決方案:

library(data.table) 
setDT(temp.all)[, quartile := cut(value, breaks = 4L, labels = paste0("Q", 1:4)), by = date] 
temp.all 
  date name  value quartile 
1: 2017-08-28 a -0.56047565  Q1 
2: 2017-08-28 b -0.23017749  Q2 
3: 2017-08-28 c 1.55870831  Q4 
4: 2017-08-28 d 0.07050839  Q2 
5: 2017-08-28 e 0.12928774  Q2 
6: 2017-08-28 f 1.71506499  Q4 
... 
18: 2017-08-29 f -1.96661716  Q1 
19: 2017-08-29 g 0.70135590  Q3 
20: 2017-08-29 h -0.47279141  Q2 
21: 2017-08-29 i -1.06782371  Q1 
22: 2017-08-29 j -0.21797491  Q2 
23: 2017-08-29 k -1.02600445  Q2 
24: 2017-08-29 l -0.72889123  Q2 
      date name  value quartile 

注意,結果被date分組通過的要求OP而不是day(date),其將聚合月的日期,例如1月1日,2月1日,3月1日等。

進一步注意,只有一個附加列quartile被添加到temp.all原地,即沒有複製整個數據集以節省存儲器和時間(這可能在處理大數據集時變得相關)。