以更快的方式计算 R 中列中不同 id 的特征

Posted

技术标签:

【中文标题】以更快的方式计算 R 中列中不同 id 的特征【英文标题】:Count features for different ids in columns in R in faster way 【发布时间】:2014-07-09 04:02:39 【问题描述】:

我正在尝试在 R 中处理一个 20 GB 的数据文件。我有 16 GB 的 RAM 和 i7 处理器。我正在使用以下方法读取数据:

y<-read.table(file="sample.csv", header = TRUE, sep = ",", skip =0, nrows =50000000)

数据集'y'如下:

id    feature

21    234
21    290
21    234
21    7802
21    3467
21    234
22    235
22    235
22    1234
22    236
22    134
23    9133
23    223
23    245
23    223  
23    122
23    223 

以上是示例数据集,它显示了特定 id 的不同特征。我想计算另一个数据集 x 中列出的特定特征对于 y 中的 id 出现了多少次。

数据集x如下:

id    feature

   21      234
   22      235
   23      223

我想要的最终输出如下:

 id    feature_count

   21      3
   22      2
   23      3

我们看到 234 在 21 中出现了三次,235 在 22 中出现了两次,223 在 23 中出现了两次。

为此,我尝试获取新 id 开始的位置:(例如,上述示例的第 1、第 7 和第 12 位置),然后使用 for 循环计算特征,如下所示:

获取不同id的位置

positions=0
positions[1]=1
j=2
for(i in 1:50000000)
    if(y$id[i]!=y$id[i+1])
    positions[j]=i+1
    j=j+1
  

由于数据量很大,因此循环需要花费大量时间。(对于 5000 万行,在上述配置 PC 上需要 321 秒,而我有 3 亿行)。

计算与 'x' 中给定特征匹配的特征。(x 是上面指定的数据框,特征将从该数据框与 y 的特征匹配。匹配时 feature_count 增加)

for(i in 1 :length(positions))
  for(j in positions[i]:positions[i+1])
    if(y$feature[j]==x$feature[i])         
       feature_count[i]=feature_count[i]+1
    
  

是否有任何 R 函数可以在更快的时间内为我共同完成这项工作。 同样使用“positions[i]:positions[i+1]”递增 for 循环会引发错误,指出 for 循环中的 NA 参数。请提出一个正确的方法来做到这一点。

【问题讨论】:

【参考方案1】:

我承认我并不真正理解问题的编写方式,但听起来“data.table”将是可行的方法,您应该查看.N 函数。正如已经提到的fread 将比read.csv 好得多,所以我假设您已经将数据读入名为“DT”的data.table

这是一个小的:

DT <- data.table(id = c(rep(21, 6), rep(22, 5), 23, 23),
                 feature = c(234, 290, 234, 7802, 3467, 234, 235,
                             235, 1234, 236, 134, 9133, 223))
DT
#     id feature
#  1: 21     234
#  2: 21     290
#  3: 21     234
#  4: 21    7802
#  5: 21    3467
#  6: 21     234
#  7: 22     235
#  8: 22     235
#  9: 22    1234
# 10: 22     236
# 11: 22     134
# 12: 23    9133
# 13: 23     223

如果您只想计算每个独特功能的数量,您可以这样做:

DT[, .N, by = "id,feature"]
#     id feature N
#  1: 21     234 3
#  2: 21     290 1
#  3: 21    7802 1
#  4: 21    3467 1
#  5: 22     235 2
#  6: 22    1234 1
#  7: 22     236 1
#  8: 22     134 1
#  9: 23    9133 1
# 10: 23     223 1

如果你想要first“特征”的计数,通过“id”,你可以使用:

DT[, .N, by = "id,feature"][, .SD[1], by = "id"]
#    id feature N
# 1: 21     234 3
# 2: 22     235 2
# 3: 23    9133 1

如果您想通过“id”获取最常出现的“特征”(在这种情况下与上面的结果相同),您可以尝试以下操作:

DT[, .N, by = "id,feature"][, lapply(.SD, function(x) x[which.max(N)]), by = "id"]

更新

根据您的新描述,这似乎容易多了。

只需merge 您的数据集和aggregate 计数。再次,在“data.table”中快速完成:

DTY <- data.table(y, key = "id,feature")
DTX <- data.table(x, key = "id,feature")
DTY[DTX][, .N, by = id]
#    id N
# 1: 21 3
# 2: 22 2
# 3: 23 3

或者:

DTY[, .N, by = key(DTY)][DTX]
#    id feature N
# 1: 21     234 3
# 2: 22     235 2
# 3: 23     223 3

这是假设“x”和“y”定义如下:

x <- structure(list(id = 21:23, feature = c(234L, 235L, 223L),
  counts = c(3L, 2L, 3L)), .Names = c("id", "feature", "counts"),
  row.names = c(NA, -3L), class = "data.frame")
y <- structure(list(id = c(21L, 21L, 21L, 21L, 21L, 21L, 22L, 22L, 
  22L, 22L, 22L, 23L, 23L, 23L, 23L, 23L, 23L), feature = c(234L,
  290L, 234L, 7802L, 3467L, 234L, 235L, 235L, 1234L, 236L, 134L,
  9133L, 223L, 245L, 223L, 122L, 223L)), .Names = c("id", "feature"),
  class = "data.frame", row.names = c(NA, -17L))

【讨论】:

感谢您的回答。但我一开始并不知道我需要找到的特征的位置。正如您在第二部分中提到的那样,它找到了第一个特征的出现。但我不知道该特征在“id-feature”数据框中的位置。我只知道要查找哪些功能存储在不同的数据框中。假设要查找的特征存储在x中,这个命令怎么写:DT[, .N, by = "id,feature"][, .SD[1], by = "id"] 我已经编辑了这个问题,通过指定 x 数据框中的内容以及最终输出应该是什么。我觉得很清楚。等待您的答复...... 您好,谢谢您的回答。我对R真的很陌生。你能推荐我吗,我可以在哪里学习编写这样的命令......? .N 和 .SD 参数对我来说是新的。 @ShreedharPawar,它们是“data.table”包的一部分,并记录在安装和加载包时可用的手册页和小插图中。 @ShreedharPawar,正如此处另一个答案所指出的那样,20GB CSV 不一定需要 R 中的 20GB 内存。【参考方案2】:

我会为此推荐 data.table 包(fread 非常快!),然后设置一个循环,循环遍历一次读取块的文件并存储特征计数总和。这是我用于循环文件的函数的一些改编行,它可能无法按原样工作,但您可以了解该怎么做

require(data.table)
LineNu <- as.numeric(gsub(" .+","",system2("wc",paste("-l",your.file,sep=" "),stdout=TRUE, stderr=TRUE)))
DT <- fread(your.file,nrows=50000000,sep=",",header=TRUE)
KEEP.DT <- DT[,list("feature"=sum(length(feature))),by=id]
rm(DT) ; gc()
Starts <- c(seq(50000000,LineNu,by=50000000),LineNu)
for (i in 2:(length(Starts)-1)) 
  cat(paste0("Filtering next 50000000 lines    ", i, " of ",length(Starts)-1, " \n"))
  DT <- fread(your.file,skip=Starts[i],nrows=ifelse(50000000*(i-1) < Starts[length(Starts)],50000000,(50000000*(i-1)) - Starts[length(Starts)]),sep=",",header=FALSE)
  DT[,list("feature"=sum(length(feature))),by=id]
  KEEP.DT <- rbind(KEEP.DT,DT)
  rm(DT) ; gc()

您可能需要重做 DT[sum(length)] 部分,因为某些 id 可能会以不同的块读取。

【讨论】:

使用data.table 不需要循环。 fread 与大多数 by 处理一样快。 我认为需要循环,因为文件是 20Gb,他有 16Gb 的 RAM。 20GB CSV 并不一定意味着 R 内存中有 20GB 内存【参考方案3】:

你的例子:

apply(sign(table(y)), 1, sum)
21 22 23 
 4  4  2 

【讨论】:

【参考方案4】:

table() 怎么样?

> set.seed(5)
> ids <- sample(1:3, 12, TRUE)
> features <- sample(1:4, 12, TRUE)
> cbind(ids, features)
      ids features
 [1,]   1        2
 [2,]   3        3
 [3,]   3        2
 [4,]   1        1
 [5,]   1        2
 [6,]   3        4
 [7,]   2        3
 [8,]   3        4
 [9,]   3        4
[10,]   1        3
[11,]   1        1
[12,]   2        1

> table(ids, features)
   features
ids 1 2 3 4
  1 2 2 1 0
  2 1 0 1 0
  3 0 1 1 3

例如,特征 4 在 id 3 中出现 3 次。

编辑:您可以使用 as.data.frame() 来“展平”表格并获得:

> as.data.frame(table(ids, features))
   ids features Freq
1    1        1    2
2    2        1    1
3    3        1    0
4    1        2    2
5    2        2    0
6    3        2    1
7    1        3    1
8    2        3    1
9    3        3    1
10   1        4    0
11   2        4    0
12   3        4    3

【讨论】:

我需要使用 feature_count 作为数据框进行进一步处理。 table() 只会给出一个表示。

以上是关于以更快的方式计算 R 中列中不同 id 的特征的主要内容,如果未能解决你的问题,请参考以下文章

计算R中列中NA的前后值的平均值

获取bash中列中唯一值的计数

拆分python中不同列中列内的字典列表

特征缩放以相同比例转换列中的不同值

如何删除R中两列中具有相同值但ID不同的行[重复]

R,按另一列中的值缩放直方图计数