R数据表按两列分组和迭代

3

我是新手使用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图书表,结果应为:

((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-1、2-2、3-3 不应该是每本书的读者数量吗(分别为3、2、1)? - BrodieG
提供代码时,最好能够复制粘贴,你知道的,不要在行首加上 >+ - Frank
3个回答

3
以下是另一种选择:

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 - zork
2
有关 allow.cartesian 的详细信息请参见 ?data.table。有关 dcast 的详细信息请参见 ?dcast.data.table?reshape2::dcast。此外,为了更容易理解,请逐步运行每个步骤(即首先运行 bt[data.table(books=c(combs), combo.id=c(col(combs))), allow.cartesian=T],然后添加下一个步骤等)。 - BrodieG
如果您可以口头描述解决方案背后的思路,包括中间步骤,那么理解整个过程将会更容易。我正在尝试从代码中理解每个单独的步骤,但是如果没有清晰的了解每个步骤应该实现什么,这将会很困难。谢谢! - zork
@zork,这里有很多需要解释的内容。也许你可以先阅读一下 data.table 的简介 **vignette**。 - BrodieG

1

try this:

## 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)

另一种输出形式是将输出作为数据表格,并给出每个读者阅读的书籍数量以及每个读者读的书籍数量:
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 代码 与基本函数产生的结果不同。 - Pierre L
我知道,这就是为什么我写了“与data.table输出相同”,第二种形式更有意义,但OP提到想要将输出作为列表。 - grrgrrbla
布局不是唯一的不同之处。提供的信息也是不同的。 - Pierre L
不错。我正在编写一个表达式,以查看是否阅读了不同的书籍组合,正如OP所请求的那样。 - Pierre L
做那个,我不知道如何以 OP 想要的确切方式完成,所以渴望学习。 - grrgrrbla
显示剩余4条评论

-1
这是一个基于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”。 - zork

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接