【问题标题】:How to get data according to stock code and date while avoiding for loops?如何在避免for循环的同时根据股票代码和日期获取数据?
【发布时间】:2017-06-10 02:34:22
【问题描述】:

我有两个数据框。 data1由三栏组成,一栏是股票代码,如600287,一栏是其盈利公告日,如2015-09-07,第三栏是其公告盈利(每股收益)如0.8。这个数据框是随机排列的,它由不同的股票和不同的公告日组成,因为它涵盖了从 2014 年到 2016 年的时间。每只股票一年可以发布 4 次,我在这个数据框中有 2400 只股票。

code1     day1       announcement
600181   2015-09-08       0.9

data2 是股票每日表现数据。从 2014 年到 2015 年,它的每日收益率为 2500 只股票。所以它有超过 200 万行,这就是我寻找有效解决方案的原因。 data2 也有代码和日期。

code2      day2        return
600298    2016-08-09    0.03

我正在研究公司发布公告后的股价反应。基本上,例如,如果公司“A”在 2016 年 9 月 8 日宣布盈利,我必须知道股票“A”在未来 5 个交易日(包括 2015 年 9 月 8 日,如果是天)。每只股票的交易日都不同,但当且仅当这一天出现在data2 中时,它才是股票“A”的交易日。

这里的困难在于股票“A”在 2015-06-09 宣布,但这一天没有出现在股票“A”的data2(这可能是因为 2015-06-09 是周日在中国不是交易日)。我所做的是使用 difftime() 函数然后订购它,但这很慢!

我想得到的最终数据框是这样的(7列)

code     announce-day    d1      d2     d3      d4     d5
600287    2015-08-07     0.08   0.06   0.02    0.01  -0.02

(再次我想说,如果这一天是交易日,第 1 天可能是 2015-08-07。也可能是 2015-08-09。唯一的判断是它在 2015-08 之后首先出现在 data2 -07)

我已经解决这个问题很长时间了,但我无法解决它。 我举个简单的例子。

code1<-"600187"
day1<-as.Date("2016-10-09")  ##stock 600187 announce on 2016-10-09
announcement<-0.8
data1<-data.frame(code1, day1,announcement)
code2<-c(rep("600187",10),"600234")
x<-as.Date("2016-07-08")
x<-seq(x,x+4,by=1)
y<-as.Date("2016-10-11")
y<-seq(y,y+4,by=1)
day2<-c(x,y,as.Date("2016-12-30"))
return<-"whatever"
data2<-data.frame(code2,day2,return)

在这种情况下,data1 只包含一只股票的一份公告。宣布日期是 2016-10-09,但出现在 data2 的第二天是 2016-10-11。

这是我的for循环代码,我仍然使用测试数据,因为我不知道如何上传整个数据。

require(snow)
code1<-c("600187","600111","600111")
day1<-as.Date(c("2016-10-09","2011-02-02","2011-09-09"))
announcement<-c(0.8,0.2,0.2)
data1<-data.frame(code1,day1,announcement,stringsAsFactors=FALSE)
code2<-c(rep("600187",10),"600234")
x<-as.Date("2016-07-08")
x<-seq(x,x+4,by=1)
y<-as.Date("2016-10-11")
y<-seq(y,y+4,by=1)
day2<-c(x,y,as.Date("2016-12-30"))
return<-seq(from = 0.01, by = 0.005, length.out = length(day2))
data2<-data.frame(code2,day2,return,stringsAsFactors=FALSE)
mtl<-function(ichunk,data2,data1){
stime<-data1$day1
 cd<-data1$code1
k<-1
houxu<-data.frame(cd=NA,date=NA,l1=NA,l2=NA,l3=NA,l4=NA,l5=NA)
  for(i in ichunk){
a<-subset(data2,code2==cd[i])
   a<-transform(a,time=difftime(day2,stime[i],units="days"))
a<-subset(a,time>=0)
 a<-subset(a,rank(time)%in%1:5)
 a<-a[order(a$time),] 
  q<-c(cd[i],1,a$return)   ##the 1 is used for date, 
 if(length(q)<7)
  { houxu[k,]<-NA} else {houxu[k,]<-q}     
  k<-k+1}
 houxu[,2]<-stime[ichunk]  ##the column of day 
return(houxu)}
mutlinks<-function(cls,data2,data1){
  n<-nrow(data1)
 options(warn=-1) 
 k<-ceiling(n/2)
ichunks<-list(1:k,(k+1):n)
options(warn=0) 
  df<-clusterApply(cl=cls,fun=mtl,ichunks,data2,data1) 


 do.call(rbind,df) }

cl<-makeCluster(type="SOCK",c("localhost","localhost")) 
bxdf<-mutlinks(cl,data2,data1)  
bxdf<-na.omit(bxdf)

这段代码需要 16 分钟,不会太长

【问题讨论】:

  • for-loop 在这里不起作用。我需要一个智能使用矢量化的解决方案
  • 使用data.table 的滚动加入,无需任何for 循环即可完成任务。
  • 在您的一些 cmets 回答中,您提到您有一个使用 for 循环的代码,它返回正确的结果。拜托,你能edit你的问题并添加你正在使用的完整代码吗?谢谢。
  • 我已经发布了我的代码,仍然使用一些测试数据

标签: r


【解决方案1】:

根据 OP,data2从 2014 年到 2015 年的每日回报率为 2500 支股票,超过 200 万行

我推荐使用data.table 包来完成这项任务,原因有两个:它专为大数据的快速连接而设计,它允许我们使用滚动连接。此任务无需使用for 循环。

因此,使用 OP 给出的样本数据集,data.table 解决方案

library(data.table)   # CRAN version 1.10.4 used
# coerce to data.table, 
# set keys to make sure data are properly ordered,
# add column to join on
setDT(data1, key = c("code1", "day1"))[, join_day := day1] # announcements
setDT(data2, key = c("code2", "day2"))[, join_day := day2] # returns

# join on stock code and do a rolling join on day
data1[data2, on = c(code1 = "code2", "join_day"), roll = TRUE]

返回

     code1       day1 announcement   join_day       day2   return
 1: 600187       <NA>           NA 2016-07-08 2016-07-08 whatever
 2: 600187       <NA>           NA 2016-07-09 2016-07-09 whatever
 3: 600187       <NA>           NA 2016-07-10 2016-07-10 whatever
 4: 600187       <NA>           NA 2016-07-11 2016-07-11 whatever
 5: 600187       <NA>           NA 2016-07-12 2016-07-12 whatever
 6: 600187 2016-10-09          0.8 2016-10-11 2016-10-11 whatever
 7: 600187 2016-10-09          0.8 2016-10-12 2016-10-12 whatever
 8: 600187 2016-10-09          0.8 2016-10-13 2016-10-13 whatever
 9: 600187 2016-10-09          0.8 2016-10-14 2016-10-14 whatever
10: 600187 2016-10-09          0.8 2016-10-15 2016-10-15 whatever
11: 600234       <NA>           NA 2016-12-30 2016-12-30 whatever

滚动加入已将2016-10-09的公告复制到当天之后的所有匹配股票的回报中。这称为LOCF最后一次观察结转。它将一直这样做,直到遇到该特定股票的下一次公告。

可以使用以下方法从结果中删除带有 NA 的行:

data1[data2, on = c(code1 = "code2", "join_day"), roll = TRUE, nomatch = 0]

产生

    code1       day1 announcement   join_day       day2   return
1: 600187 2016-10-09          0.8 2016-10-11 2016-10-11 whatever
2: 600187 2016-10-09          0.8 2016-10-12 2016-10-12 whatever
3: 600187 2016-10-09          0.8 2016-10-13 2016-10-13 whatever
4: 600187 2016-10-09          0.8 2016-10-14 2016-10-14 whatever
5: 600187 2016-10-09          0.8 2016-10-15 2016-10-15 whatever

现在,OP已要求研究公告日及之后五个交易日的回报。滚动连接允许限制值的结转距离,但它适用于 difference(此处以天为单位),但不适用于 行数。由于交易日的顺序存在间隔,因此该功能不能直接用于该目的。相反,head() 用于选择每个组中的第一个 n_days

由于给定数据样本的大小有限,假设我们要观察接下来两个交易日的回报以进行演示和测试:

n_days <- 2L
joined <- data1[data2, on = c(code1 = "code2", "join_day"), roll = TRUE, nomatch = 0][
  order(day2), head(.SD, n_days), .(code1, day1)]

joined 现在缩减为包含n_days 在每只股票的每个公告日或之后的返回值:

    code1       day1 announcement   join_day       day2   return
1: 600187 2016-10-09          0.8 2016-10-11 2016-10-11 whatever
2: 600187 2016-10-09          0.8 2016-10-12 2016-10-12 whatever

最后,OP 希望将结果从长格式重新调整为宽格式。这可以使用dcast()

dcast(joined, code1 + day1 ~ paste0("d", rowid(code1, day1)), 
      value.var = "return")

产生:

    code1       day1       d1       d2
1: 600187 2016-10-09 whatever whatever

通过减少数据量可能提高性能

正如上面已经提到的,roll 参数允许我们限制值结转的距离。与nomatch = 0 一起,可用于减少滚动连接操作产生的数据量。然而,roll 参数必须谨慎选择,因为返回天数的顺序存在间隔。因此,使用了最长跳空的长度加上交易日数:

max_gap <- data2[order(day2), max(diff(day2))]
joined <- data1[data2, on = c(code1 = "code2", "join_day"), roll = max_gap + n_days,
                nomatch = 0][
                  order(day2), head(.SD, n_days), .(code1, day1)]

数据

data1 <- structure(list(code1 = structure(1L, .Label = "600187", class = "factor"), 
    day1 = structure(17083, class = "Date"), announcement = 0.8, 
    join_day = structure(17083, class = "Date")), .Names = c("code1", 
"day1", "announcement", "join_day"), row.names = c(NA, -1L), class = "data.frame")
data2 <- structure(list(code2 = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 2L), .Label = c("600187", "600234"), class = "factor"), 
    day2 = structure(c(16990, 16991, 16992, 16993, 16994, 17085, 
    17086, 17087, 17088, 17089, 17165), class = "Date"), return = structure(c(1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), class = "factor", .Label = "whatever"), 
    join_day = structure(c(16990, 16991, 16992, 16993, 16994, 
    17085, 17086, 17087, 17088, 17089, 17165), class = "Date")), .Names = c("code2", 
"day2", "return", "join_day"), row.names = c(NA, -11L), class = "data.frame")

【讨论】:

  • 真是太棒了!!!!你是我的偶像。对于这个优秀的答案,我要学习这个包!你能给我更多关于学习这个包的建议吗,因为它在处理数据帧方面似乎与基本包完全不同
  • 加入
  • 实际上我使用了for循环,最终的data.frame有9033行。但加入
  • 我已经测试了我的 for 循环(使用并发计算),我认为它是正确的。顺便说一句,你的代码很快,所以我真的很想学习!
  • 实际上我合并了 for-loop-final-data 和 your-method-final-data,我发现它们有 7500 行相同
【解决方案2】:

我编写的 for 循环为您提供了示例所需的结果。但是我不知道它的性能对于你的大数据集是否可以接受。不过,我确信有很多方法可以优化循环。

code1<-"600187"
day1<-as.Date("2016-10-09")  ##stock 600187 announce on 2016-10-09
announcement<-0.8
data1<-data.frame(code1, day1,announcement)
code2<-c(rep("600187",10),"600234")
x<-as.Date("2016-07-08")
x<-seq(x,x+4,by=1)
y<-as.Date("2016-10-11")
y<-seq(y,y+4,by=1)
day2<-c(x,y,as.Date("2016-12-30"))
return<-seq(from = 0.01, by = 0.005, length.out = length(day2))
data2<-data.frame(code2,day2,return)


df3 <- data.frame(data1$code1, data1$day1, NA, NA, NA, NA, NA)
colnames(df3) <- c("code", "announce-day", "r1", "r2", "r3", "r4", "r5")
`%notin%` <- function(x,y){
  !(x %in% y) 
}
for(i in 1:nrow(df3)){
  data.code <- data.frame(data2$day2[as.numeric(data2$code2) ==
                          as.numeric(df3$code[i])],
                          data2$return[as.numeric(data2$code2) ==
                          as.numeric(df3$code[i])])
  colnames(data.code) <- c("day","return") 
  start.date <- df3$`announce-day`[i]
  while(start.date %notin% data.code$day){
    start.date <- start.date + 1
  }
  index <- which(start.date == data.code$day)[1]
  df3$r1[i] <- data.code$return[index]
  df3$r2[i] <- data.code$return[index + 1]
  df3$r3[i] <- data.code$return[index + 2]
  df3$r4[i] <- data.code$return[index + 3]
  df3$r5[i] <- data.code$return[index + 4]
}

【讨论】:

  • 差不多了。也许我没有说清楚。如果 A 公司在 2016-09-09 宣布,假设现在是星期五,可以包括这一天。但是第二天是星期六,不是交易日。所以 df3$r2[i]
  • 所以我像这样更改for循环` data.code
  • for 循环不起作用。 30分钟过去了,仍然没有结果。 data1 有 9000 行,data2 有 200 万行。
  • 我看到了问题 - 错过了周末也可能扰乱回归系列。我已经编辑了原始答案以解释这一点。就个人而言,我会尝试在循环中避免order。我可以想象它对于这么多行来说是计算密集型的。只要确保您正确订购了data2。如果它仍然太慢,则必须有人想出一个智能地使用矢量化的解决方案。
猜你喜欢
  • 2018-04-02
  • 2019-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多