R:分组依据并将通用函数应用于两列

R: Group by and Apply a general function to two columns

您好,我想对两个数据框列进行分组,并将一个函数应用于另外两个数据框列。 例如,

ticker <- c("A", "A", 'A', "B", "B", "B")
date <- c(1,1,2,1,2,1)
ret <- c(1,2,4,6,9,5)
vol <- c(3,5,1,6,2,3)
dat <- data.frame(ticker,date,ret,vol)

对于每个代码和每个日期,我想计算其 PIN。

现在,为了避免进一步的混淆,也许只说出实际功能会有所帮助。 YZ 是 InfoTrad 包中的一个函数,YZ 只接受具有两列的数据框。它使用一些优化工具和 returns 估计的 PIN。

install.packages(InfoTrad)
library(InfoTrad)
get_pin_yz <- function(data) {
  return(YZ(data[ ,c('volume_krw_buy', 'volume_krw_sell')])[['PIN']])
}

我知道如何在 R 中使用 for 循环执行此操作。但是 for 循环的计算成本非常高,可能需要数周才能完成 运行 我的大型数据集。因此,我想问一下如何使用groupby来做到这一点。

# output format is wide wrt long format as "dat"
dat_w <- data.frame(ticker = NA, date = NA, PIN = NA)
for (j in c("A", "B")){
  
  for (k in c(1:2)){
    
    subset <- dat %>% subset((ticker == j & date == k), select = c('ret', "vol"))
    new_row <- data.frame(ticker = j, date = k, PIN = YZ(subset)$PIN)
    dat_w <- rbind(dat_w, new_row)
  }
}
dat_w <- dat_w[-1, ]
dat_w

不知道这是否可以帮助你帮助我 -- 我知道如何在 python 中做到这一点:我只是写了一个函数和 运行 df.groupby(['ticker','date']).apply(function).

最后,想要的数据框是:

ticker <- c('A','A','B','B')
date <- c(1,2,1,2)
PIN <- c(1.05e-17,2.81e-09,1.12e-08,5.39e-09)
data.frame(ticker,date,PIN)

有人能帮忙吗?

谢谢!

最佳,

达西


以前的东西(随意忽略) 以前,我这样写: 我的函数是:

get_rv <- function(data) {
  return(data[['vol']] + data[['ret']])
}

我想要的是:

ticker_wanted <- c('A','A', 'B', 'B')
date_wanted <- c(1,2,1,2)
rv_wanted <- c(7,5,10,11)
df_wanted <-data.frame(ticker_wanted,date_wanted,rv_wanted)

但这并不是我的实际功能。 vol+ret 只是一个例子。我对更一般的情况更感兴趣:如何分组并将通用函数应用于两个或多个数据帧。我使用 vol + ret 只是因为我不想让别人在他们的 PC 上安装一些可能不相关的软件包来打扰他们。

根据 real-life 示例更新:

您可以采用如下直接方法:

library(tidyverse)
library(InfoTrad)
dat %>%
  group_by(ticker, date) %>%
  summarize(PIN = YZ(as.data.frame(cur_data()))$PIN)

# A tibble: 4 x 3
# Groups:   ticker [2]
  ticker  date      PIN
  <chr>  <dbl>    <dbl>
1 A          1 1.05e-17
2 A          2 1.56e- 1
3 B          1 1.12e- 8
4 B          2 7.07e- 9

这里的困难在于 YZ 函数只接受真实的数据帧,不接受 tibbles 并且它 returns 几个值,而不仅仅是 PIN。

从理论上讲,您可以将其包装到您自己的函数中,然后 运行 您自己的函数,就像我在下面的示例中展示的那样,但也许这种方式已经可以解决问题了。

我也不希望它 运行 比 for 循环快得多。看来这个YZ函数有一些more-than-linear 运行的时间,所以传递更大的数据量还是需要一些时间。您可以尝试从一小组数据开始,然后通过将数据大小增加 10 倍来重复它,然后检查它有多快 运行s。


在你的例子中,你可以这样做:

my_function <- function(data) {
  data %>%
    summarize(rv = sum(ret, vol))
}

library(tidyverse)
df %>%
  group_by(ticker, date) %>%
  my_function()

# A tibble: 4 x 3
# Groups:   ticker [2]
  ticker  date    rv
  <chr>  <dbl> <dbl>
1 A          1     7
2 A          2     5
3 B          1    10
4 B          2    11

但是正如我在评论中提到的,我不确定这个一般示例是否对您的 real-life 用例有帮助。

也可能是您不需要创建自己的函数,因为 built-in 函数已经存在。就像在示例中一样,您最好直接总结而不是将其包装到函数中。

你能做到吗? (以 summarize 作为您的功能的示例):

ticker <- c("A", "A", 'A', "B", "B", "B")
date <- c(1,1,2,1,2,1)
ret <- c(1,-2,4,6,9,-5)
vol <- c(3,5,1,6,2,3)
df <- data.frame(ticker,date,ret,vol)

df_wanted <- get_rv(df)

get_rv <- function(data){
  result <- data %>%
    group_by(ticker,date) %>%
    summarise(rv =sum(ret) + sum(vol)) %>%
    as.data.frame()
  names(result) <- c('ticker_wanted', 'date_wanted', 'rv_wanted')
  return(result)
}    

假设你的dataframe如下:

data <- data.frame(ticker,date,ret,vol)

使用 split 根据股票代码和日期的值将您的数据帧拆分为一组数据帧。

dflist = split(data, f = list(data$ticker, data$date), drop = TRUE)

现在使用 lapplysapply 到 运行 dflist 的每个数据帧成员上的函数 YZ()。

pins <- lapply(dflist, function(x) YZ(x)$PIN)