【问题标题】:How to apply function to each group of panel data (with data.table?) in R?如何将函数应用于 R 中的每组面板数据(使用 data.table?)?
【发布时间】:2017-04-11 19:04:22
【问题描述】:

我对 R 有基本的了解,我尝试在数据框上自动进行一些计算。我创建了一个函数和一些代码,并希望得到一些帮助,以使一切与 R 哲学保持一致。

我有一个面板数据集 df,您可以这样构建:

# sample data frame
id <- c("i","i","i","j","j","j","k","k")
time <- c(1,2,3,1,2,3,1,2)
b1 <- c(1,0,1,0,0,1,1,0)
b2 <- c(0,0,1,0,0,0,1,1)
b3 <- c(0,1,0,1,0,0,0,0)
b4 <- c(0,0,0,0,1,0,1,1)
df <- data.frame(id,time,b1,b2,b3,b4)

我使用 data.table 对其进行转换:

# data.table
### set-up
dt <- data.table(df)
setkey(dt,id,time)

### lead
nm1 <- grep("^b", colnames(dt), value=TRUE)
nm2 <- paste("lead", nm1, sep=".")
dt[, (nm2) := shift(.SD, type='lead'), by = id, .SDcols=nm1]

现在,我想为每个组 id 计算一个矩阵,将所有从一行到下一行的转换相加。每个矩阵都存储在一个列表中。我创建了一个应用于每个组的函数:

# empty list
m.out <- list()

# group i
m <- matrix(0,cat,cat + 1)
dt1 <- dt["i",c(nm1,nm2),with=FALSE]
m.out[[1]] <- calcMatrix(dt1)

# group j
m <- matrix(0,cat,cat + 1)
dt1 <- dt["j",c(nm1,nm2),with=FALSE]
m.out[[2]] <- calcMatrix(dt1)

# group k
m <- matrix(0,cat,cat + 1)
dt1 <- dt["k",c(nm1,nm2),with=FALSE]
m.out[[3]] <- calcMatrix(dt1)

如何应用该函数并为 data.table 的所有组创建矩阵列表(特别是如果我在大数据集上尝试代码)?

我想到了这个解决方案,但它不起作用。该函数本身不会为每个 .SD 创建一个矩阵,并且列表未正确附加:

m.out <- list()
m.out <- dt[,calcMatrix(.SD),by = id, .SDcols = c(nm1,nm2)]

函数calcMatrix定义如下:

calcMatrix <- function(x) {

  # number of "b" categories
  cat <- length(nm1)
  # vector of column indices
  col.index <- grep("^b",colnames(x))
  # number of rows in the data.table x
  row.num <- nrow(x)

  # fill in matrix
  m <- matrix(0,cat,cat + 1)
  for(i in col.index) {
    for(j in 1:(row.num - 1)) {
      m[i,] = m[i,] + as.integer(x[j,i,with=FALSE]) * c(0,as.matrix(x[j, .SD, .SDcols = nm2]))
    }
   m[i,1] = m[i,1] + as.integer(x[row.num,i,with=FALSE])
  }
  return(m)
}

由于有两个循环,此函数可能未针对 R 进行优化。有没有办法摆脱循环?

编辑:我可以在calcMatrix 中解释我的工作。

  1. 对于每个组id,我想获得一个矩阵,其数量为 bi 变量作为行,bi 变量的数量 +1 作为列。我会统计每组的转换次数id
  2. 然后我取出每个 bi 并检查下一个 时间 到达哪个 bj(基本上是从 bi em> 到 bj)。
  3. 然后我在单元格 m[i,j+1] 处的矩阵中执行 +1(第一列用于最后一行)。
  4. 当我们在最后一行(最后一个时间)时,没有过渡,所以如果 bi=1 那时时间,我在第一列中 +1(自身转换)。

这样,我计算了从 bibj 的所有转换以及所有最后的状态。这就是我使用shift 函数计算lead 的原因。我可以直接将前导行添加到矩阵中。我想知道这是否可以在不循环但通过矢量化的情况下以不同的方式编写,因为它是 R 中的哲学。

【问题讨论】:

  • 拜托,您能否提供有关您想要实现的目标的更多信息?我盯着你的代码(shiftcalcMatrix)但我不明白。
  • 首先,感谢 Uwe Block 的回答。您回答了将过程扩展到任何数据集最重要的是什么。
  • 第二,这是我在上面的文字中的编辑,以获取更多关于calcMatrix的详细信息。

标签: r function matrix data.table


【解决方案1】:

实际上有两个问题。只能回答一个。第二个关于优化功能需要额外的信息。

如何应用该函数并为 data.table 的所有组创建矩阵列表?

您可以尝试lapply() 创建结果列表:

lapply(dt[, unique(id)], function(.id) {calcMatrix(dt[id == .id, c(nm1,nm2), with=FALSE])})

返回:

[[1]]
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    0    0    1    0
[2,]    1    0    0    0    0
[3,]    0    1    1    0    0
[4,]    0    0    0    0    0

[[2]]
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    0    0    0    0
[2,]    0    0    0    0    0
[3,]    0    0    0    0    1
[4,]    0    1    0    0    0

[[3]]
     [,1] [,2] [,3] [,4] [,5]
[1,]    0    0    1    0    1
[2,]    1    0    1    0    1
[3,]    0    0    0    0    0
[4,]    1    0    1    0    1

【讨论】:

  • 这里有两个变体:lapply(split(dt, dt[,id]), function(x) calcMatrix(x[, c(nm1,nm2), with=FALSE]))lapply(split(dt[, c(nm1,nm2), with=FALSE], dt[,id]), calcMatrix)。包data.table有自己的split()-function。
猜你喜欢
  • 1970-01-01
  • 2023-03-06
  • 2013-03-18
  • 1970-01-01
  • 2014-05-11
  • 2019-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多