【问题标题】:R data.table group by and iterate by two columnsR data.table 按两列分组和迭代
【发布时间】:2015-08-04 09:08:00
【问题描述】:

我是 R 新手,正在尝试解决以下问题:

有一个表,其中包含两列 booksreaders 这些书籍,其中 booksreaders 分别是书籍和读者 ID:

> books = c (1,2,3,1,1,2)
> readers = c(30, 10, 20, 20, 10, 30)
> bt = data.table(books, readers)
> bt
   books readers
1:     1      30
2:     2      10
3:     3      20
4:     1      20
5:     1      10
6:     2      30

对于每一对书,我需要使用以下算法计算阅读这两本书的读者人数:

for each book
  for each reader of the book
    for each other_book in books of the reader
      increment common_reader_count ((book, other_book), cnt)

为了实现上述算法,我需要将这些数据分成两个列表:1)包含每本书的读者的图书列表和 2)包含每个读者阅读的图书的读者列表,例如:

> bookList = list( 
+ list(1, list(30, 20, 10)),
+         list(2, list(10, 30)),
+         list(3, list(20))
+       )
> 
> readerList = list (
+ list(30, list(1,2)),
+ list(20, list(3,1)),
+ list(10, list(2,1))
+ )
>  

问题:

1) 使用什么函数从书表中构建这些列表?

2) 从bookListreaderList 如何生成阅读这两本书的读者数量的书对?对于上述bt book 表,结果应该是:

((1, 2), 2)
((1,3), 1)
((2,3), 0)  

成对书籍的顺序无关紧要,因此,例如,(1,2)(2,1) 应该减少到其中之一。

请提供解决此问题的函数和数据结构。谢谢!

更新:

理想情况下,我需要得到一个矩阵,其中书籍 ID 既作为行又作为列。交集是阅读该对中两本书的读者的计数。所以对于上面的例子矩阵应该是:

books | 1 | 2 | 3 |
   1  | 1 | 2 | 1 |
   2  | 2 | 1 | 0 |
   3  | 1 | 0 | 1 |

   Which means:

   book 1 and 2 are read together by 2 readers 
   book 1 and 3 are read together by 1 reader
   book 2 and 3 are read together by 0 readers

如何构建这样的矩阵?

【问题讨论】:

  • 1-1、2-2、3-3不应该是每本书的读者人数(即分别为3、2、1)吗?
  • 在提供代码时,如果它可以复制粘贴会更好,你知道,在行首没有>+

标签: r group-by iteration data.table


【解决方案1】:

这是另一种选择:

combs <- combn(unique(books), 2)# Generate combos of books
setkey(bt, books)
both.read <-bt[                 # Cartesian join all combos to our data
  data.table(books=c(combs), combo.id=c(col(combs))), allow.cartesian=T
][,
  .(                            # For each combo, figure out how many readers show up twice, meaning they've read both books
    read.both=sum(duplicated(readers)), 
    book1=min(books), book2=max(books)
  ),
  by=combo.id
]
dcast.data.table(               # dcast to desired format
  both.read, book1 ~ book2, value.var="read.both", fun.aggregate=sum
)

生产:

   book1 2 3
1:     1 2 1
2:     2 0 0

请注意,这仅适用于非等效组合(即我们不显示书籍 1-2 和 2-1,仅显示 1-2,因为它们是相同的)。

【讨论】:

  • 看起来不错,谢谢!我是 R 新手,所以对使用的函数有点不知所措。在哪里阅读有关 allow.cartesiandcast 的信息?
  • 对于allow.cartesian,请参阅?data.table。对于dcast,请参见?dcast.data.table?reshape2::dcast。另外,为了更容易理解,分别运行每个步骤(即先运行bt[data.table(books=c(combs), combo.id=c(col(combs))), allow.cartesian=T],然后添加下一步,等等。
  • 如果你能口头描述你的解决方案背后的想法,包括中间步骤,就会更容易理解整个事情。我试图从代码中理解每个单独的步骤,但是如果没有清楚地理解每个步骤应该实现什么,就很难。谢谢!
  • @zork,这里有一点需要解释。也许您可以先阅读简介 data.table vignette
【解决方案2】:

这是一个基本 R 解决方案,用于测试是否读取了对。如果您绝对需要,其他人可以为data.table 添加一个:

books = c (1,2,3,1,1,2)
readers = c(30, 10, 20, 20, 10, 30)
bks = data.frame(books, readers)

cmb <- combn(unique(books), 2)
cmb <- t(cmb)
combos <- as.data.frame(cmb)
bktbl <- t(table(bks))

for (i in 1:nrow(bktbl)) {
  x[i] <- sum(bktbl[i, cmb[i, 1]], bktbl[i, cmb[i, 2]])
  combos$PairRead <- ifelse(x > 1,"yes", "no")
}
combos
  V1 V2 PairRead
1  1  2      yes
2  1  3      yes
3  2  3       no

【讨论】:

  • 请看我的问题更新 - 我需要这对书籍一起阅读的次数,而不是标志 read_together
【解决方案3】:

试试这个:

## gives you a seperate list for each book
list_bookls <- split(bt$readers, books)

## gives you a seperate list for each reader
list_readers <- split(bt$books, readers)

另一种输出形式,输出为data.table,并给出每个读者阅读的书籍数量和每个读者阅读的书籍数量:

bt[ , .("N Books" = length(unique(books))), by = readers]
bt[ , .("N Readers" = length(unique(readers))), by = readers]

对于您问题的第二部分,我将使用以下内容:

bt2 <- bt[ , .N, by = .(readers, books)]
library(tidyr)
spread(bt2, key = books, value = "N", fill = 0)

输出是一个表格,如果书被读者 X 阅读,则为 1,否则为 0:

   readers 1 2 3
1:      10 1 1 0
2:      20 1 0 1
3:      30 1 1 0

【讨论】:

  • 您的 data.table code 产生的结果与基本函数不同。
  • 我知道,这就是为什么我写“与输出相同作为 data.table”,第二种形式更有意义,但 OP 提到希望输出作为列表..
  • 布局并没有什么不同。给出的信息不同。
  • 你是对的,我编辑了它以使其更清晰,谢谢!我认为更多使用数据的方法在这里很有用,因为 OP 说“请建议函数和数据结构来解决这个问题。谢谢!”
  • 不错。我正在写一个表达式,看看是否按照 OP 的要求阅读了不同的书籍组合。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-31
  • 1970-01-01
  • 2022-11-15
相关资源
最近更新 更多