dplyr join 并在没有 NA 的情况下保持变量 obs

dplyr join and keeping variable obs without NA

我有一个 for 循环,它根据 tdata$me 和 10% 分位数分配投资组合。我遇到的问题是当我 运行 for 循环时,我最终只有最后一个观察年和分配的投资组合。当我回顾这些年时,我的想法是放置投资组合分配 portf,然后将其与更大的数据集结合起来。

我的问题是如何在不将 NA 放入所有其他未知 obs 的情况下加入两个数据集,而是保持 obs 原样?

此外,是否有更好的方法来 运行 这个 for 循环与 dplyr?这似乎是一种低效的投资组合分配方式,但我想不出其他方式。

可重现的例子:

tdata <- structure(list(cusip = c(47L, 47L, 47L, 47L, 47L, 47L, 47L, 47L, 
    47L, 47L, 47L, 47L, 47L, 47L, 47L, 47L, 47L, 47L, 47L, 47L), 
    fyear = c(1970L, 1970L, 1970L, 1970L, 1970L, 1970L, 1970L, 
    1970L, 1970L, 1970L, 1970L, 1970L, 1971L, 1971L, 1971L, 1971L, 
    1971L, 1971L, 1971L, 1971L), me = c(157,115,  45,  19, 132,  21, 147,
    191,  80, 165,  32, 100,  44, 134, 104,9, 183, 163, 109,  88), month = c(6L, 6L, 6L, 6L, 6L, 
    6L, 6L, 6L, 6L, 6L, 6L, 6L, 6L, 6L, 6L, 6L, 6L, 6L, 6L, 
    8L)), .Names = c("cusip", "fyear", "me", "month"), row.names = c(NA, 
    20L), class = "data.frame")

for(i in unique(tdata$fyear)){
  check <- filter(tdata, month == 06 & fyear == i)                         ###
  per <- quantile(check$me, c(.10, .20, .30, .40, .50, .60, .70, .80, .90))
  check$portf[check$me < per[[1]]] <- "A"
  check$portf[check$me >= per[[1]] & check$me < per[[2]]] <- "B"
  check$portf[check$me >= per[[2]] & check$me < per[[3]]] <- "C"
  check$portf[check$me >= per[[3]] & check$me < per[[4]]] <- "D"
  check$portf[check$me >= per[[4]] & check$me < per[[5]]] <- "E"
  check$portf[check$me >= per[[5]] & check$me < per[[6]]] <- "F"
  check$portf[check$me >= per[[6]] & check$me < per[[7]]] <- "G"
  check$portf[check$me >= per[[7]] & check$me < per[[8]]] <- "H"
  check$portf[check$me >= per[[8]] & check$me < per[[9]]] <- "I"
  check$portf[check$me >= per[[9]]]  <- "J"
  check <- select(check, cusip, fyear, portf)
  testcrsp <- left_join(tdata, check, by = c("cusip", "fyear"))  ######
}

更新:

一个 dplyr 解决方案,用于删除 for 循环。请注意,我删除了 length() 部分,因为我不确定如何在不重复 breaks 代码的情况下在 dplyr 上执行此操作。结果略有不同,因为它只有 returns 带有 months==6 的数据框,而不是未选择月份的所有带有 NA 的数据。

tdata3 <- tdata %>% group_by(fyear) %>%
  filter(month==6) %>% 
  mutate(portf = cut(me, labels=LETTERS[1:10], include.lowest=TRUE, breaks=(me %>% quantile(seq(0, 1, by=0.1)) %>% unique)) %>%      
  as.character) %>% ungroup

原文:

这就是我认为您想要的。它不使用 dplyr 因为您不需要它来简单地对这些年进行子集化和循环。它确实使用 cutme 列分位数拆分为字母因子。

tdata2 <- tdata
for (i in unique(tdata$fyear)) {
  thisyear <- tdata[tdata$fyear==i & tdata$month==6,]
  per <- unique(quantile(thisyear$me, seq(0, 1, by=0.1))) 
  factors <- cut(thisyear$me, breaks=per, labels=LETTERS[1:(length(per)-1)], include.lowest=TRUE)  
  tdata2$portf[tdata$fyear==i & tdata$month==6] <- as.character(factors)
}

tdata2
#    cusip fyear  me month portf
# 1     47  1970 157     6     I
# 2     47  1970 115     6     F
# 3     47  1970  45     6     C
# 4     47  1970  19     6     A
# 5     47  1970 132     6     G
# 6     47  1970  21     6     A
# 7     47  1970 147     6     H
# 8     47  1970 191     6     J
# 9     47  1970  80     6     D
# 10    47  1970 165     6     J
# 11    47  1970  32     6     B
# 12    47  1970 100     6     E
# 13    47  1971  44     6     B
# 14    47  1971 134     6     G
# 15    47  1971 104     6     D
# 16    47  1971   9     6     A
# 17    47  1971 183     6     J
# 18    47  1971 163     6     I
# 19    47  1971 109     6     E
# 20    47  1971  88     8  <NA>

# 0%   10%   20%   30%   40%   50%   60%   70%   80%   90%  100% 
#    A     B     C     D     E     F     G     H     I     J    

请注意,必须在分位数中使用唯一性,因为您可以(它发生在您编辑数据之前)具有相等的分位数,这不会被接受为 breaks 的因素。也正因为如此,如果直接输入 1:10.

,您应该使用 length(per)