使用阈值准则聚合数据

时间:2019-02-09 19:57:12

标签: r

让我们考虑可以在此处下载的Yahoo财务数据: https://finance.yahoo.com/quote/BTC-USD/history?period1=1325372400&period2=1548025200&interval=1d&filter=history&frequency=1d

您可以使用以下方法读取数据:

yahoo <- read.csv("~/Downloads/yahoo.BTC-USD.daily.csv",
                   na.strings=c("NA","NaN", " "))

这是结果数据框:

> head(yahoo)
        Date Open High  Low Close  Volume
1 2011-12-31 4.25 5.00 4.20  4.72  596240
2 2012-01-01 4.72 5.50 4.62  5.27  553045
3 2012-01-02 5.27 5.47 4.80  5.22  360357
4 2012-01-03 5.22 5.29 4.65  4.88  619170
5 2012-01-04 4.88 5.70 4.75  5.57  688717
6 2012-01-05 5.57 7.22 5.57  6.95 1130623

这些是每日蜡烛,即每一行代表一个交易日。

我想做两件事:

  1. 将此数据帧汇总到例如每周数据,将7行分组在一起:

    • Open将是7行中的第一个Open值
    • Close将是7行中的最后一个Close值
    • 高将是7行的高值的最大值
    • 低将是7行的低值的最小值
    • 体积将是体积值的总和
  2. 对于给定的音量阈值,将该数据帧聚合为几乎等体积的序列:每个音量阈值我都会有一行。

这是我使用for循环想到的:

第1点:

aggregate.candles <- function(x, candles) {
  Date <- candles$Date[x[1]]
  Open <- candles$Open[x[1]]
  High <- max(candles$High[x])
  Low <- min(candles$Low[x])
  Close <- candles$Close[tail(x, 1)]
  Volume <- sum(candles$Volume[x])

  return(data.frame(Date, Open, High, Low, Close, Volume))
}

require(zoo)

yahoo.weekly <- as.data.frame(rollapply(seq_along(yahoo$Open), FUN = aggregate.candles, candles = yahoo, width = 7, by = 7))

这就像一种魅力,但是如果您有改进之处,我将非常高兴。使用聚合函数不能做点什么吗?还是用tidyverse包装让它看起来更干净?

现在对于第2点,如果没有for循环,我找不到解决方法:

aggregate.volume <- function(candles, threshold) {
  Open <- c()
  High <- c()
  Low <- c()
  Close <- c()
  Volume <- c()

  tmpOpen <- -1
  tmpHigh <- 0
  tmpLow <- .Machine$double.xmax
  tmpClose <- 0
  tmpVolume <- 0
  for (i in seq_along(candles$Open)) {
    tmpVolume <- tmpVolume + candles$Volume[i]

    if (tmpVolume < threshold) {
      if (tmpOpen == -1)
        tmpOpen <- candles$Open[i]
      tmpHigh <- max(tmpHigh, candles$High[i])
      tmpLow <- min(tmpLow, candles$Low[i])
      tmpClose <- candles$Close[[i]]
    } else {
      Open <- c(Open, tmpOpen)
      Close <- c(Close, tmpClose)
      High <- c(High, tmpHigh)
      Low <- c(Low, tmpLow)
      Volume <- c(Volume, tmpVolume)

      tmpOpen <- -1
      tmpHigh <- 0
      tmpLow <- .Machine$double.xmax
      tmpClose <- 0
      tmpVolume <- 0
    }
  }

  return(data.frame(Open, High, Low, Close, Volume))
}

yahoo.volume.10m <- aggregate.volume(yahoo, threshold = 1e8)

是否有更优雅/更有效的方法(使用聚合函数或tidyverse / dplyr)?

我问效率,因为这可以在更大的数据集(例如一分钟的蜡烛)上完成。

1 个答案:

答案 0 :(得分:1)

要使用group by中的tidyverse,我们首先将Date进行突变以创建分组变量

library(tidyverse)
library(lubridate)

yahoo <- as.tibble(read.csv("~/Downloads/BTC-USD.csv", na.strings=c("NA","NaN", " ")))
yahoo <- yahoo[order(yahoo$Date),]

yahoo.weekly <- yahoo %>% 
  mutate(week = isoweek(Date), year = isoyear(Date)) %>% 
  group_by(year, week)  %>% 
  summarise("Open" = first(Open), "High" = max(High), "Low" = min(Low), "Close" = last(Close), "Volume" = sum(Volume))

cumsum_group <- function(x, threshold){
  cumsum <- 0
  groups <- rep(0, length(x))

  for (i in 1:length(x)){
    cumsum <- cumsum + x[i]

    if(cumsum >= threshold & i<length(x)){
      i <- i+1
      groups[i] <- 1
      cumsum <- 0
    }
  }
  cumsum(groups)+1
}

yahoo.volume.10m <- yahoo %>%
  mutate(group = cumsum_group(Volume, threshold = 1e8)) %>%
  group_by(group) %>% 
  summarise("Open" = first(Open), "High" = max(High), "Low" = min(Low), "Close" = last(Close), "Volume" = sum(Volume))

cumsum_group在此创建ID,以将其分组到特定阈值。不幸的是,我也不能考虑阈值“问题”的总和的变化。