R - 跨不同长度数据帧的多个标准和

时间:2018-04-30 10:13:48

标签: r sum apply sapply sumifs

第一篇文章,长期用户。

我正在尝试根据不同长度的另一个数据帧中的每个ID的2个条件有效地对列进行求和。以下是一个例子:

   ID
1  A 
2  B
3  C

ID   Color   Type  Price
A  Green   1     5
A  Blue    2     6
B  Green   3     7
B  Blue    2     2
C  Green   2     4
C  Blue    4     5

对于每个ID,如果颜色为蓝色且类型为2,我想总结价格。结果希望如下:

   ID  Price
1  A   6
2  B   2
3  C   0

这似乎是一件容易的事,但由于某些原因我无法弄明白。此外,我需要在2个大型数据集(每个> 1,000,000行)上执行此操作。我已经创建了一个函数并在循环中使用它来处理此类先前的问题,但由于信息量的原因,该解决方案不起作用。我觉得apply中的函数可能是最好的,但是我不能让它们起作用。

2 个答案:

答案 0 :(得分:2)

我改变了你的数据示例,所以它考虑到并非所有ID都在第一个数据框中,并且有两个值可以在其中进行求和:

df1 <- data.frame(ID = c("A","B","C"))

df2 <- read.table(text = "
                  ID   Color   Type  Price
                  A  Green   1     5
                  A  Blue    2     6
                  A  Blue    2     4
                  B  Green   3     7
                  B  Blue    2     2
                  C  Green   2     4
                  C  Blue    4     5
                  D  Green   2     2
                  D  Blue    4     8
                  ",header = T)

快速执行大数据框架的两个主要包是dplyrdata.table。它们非常相同(几乎见data.table vs dplyr: can one do something well the other can't or does poorly?)。以下是两种解决方案:

library(data.table)

setDT(df2)[ID %in% unique(df1$ID), .(sum = sum(Price[ Type == 2 & Color == "Blue"])),by = ID]

   ID sum
1:  A  10
2:  B   2
3:  C   0

你可以做到

setDT(df2)[ID %in% unique(df1$ID) & Type == 2 & Color == "Blue", .(sum = sum(Price)),by = ID]

但是您将丢弃C,因为不符合行选择的整个条件:

   ID sum
1:  A  10
2:  B   2

和dplyr:

library(dplyr)

df2 %>%
  filter(ID %in% unique(df1$ID)) %>%
  group_by(ID) %>%
  summarize(sum = sum(Price[Type==2 & Color=="Blue"]))

# A tibble: 3 x 2
  ID      sum
  <fct> <int>
1 A        10
2 B         2
3 C         0

答案 1 :(得分:0)

sapply版本。它可能存在更优雅的编写方式,但如果你有大表,你可以很容易地将其并行化。

使用@denis提出的数据:

df1 <- data.frame(ID = c("A","B","C"))

df2 <- read.table(text = "
                  ID   Color   Type  Price
                  A  Green   1     5
                  A  Blue    2     6
                  A  Blue    2     4
                  B  Green   3     7
                  B  Blue    2     2
                  C  Green   2     4
                  C  Blue    4     5
                  D  Green   2     2
                  D  Blue    4     8
                  ",header = T)

这是一个简单的函数,可以使用sapply执行您想要的操作:

 getPrices <- function(tableid=df1,tablevalues=df2,color="Blue",type=2){
     filteredtablevalues <- droplevels(tablevalues[ tablevalues$Color == "Blue" & tablevalues$Type == 2 & tablevalues$ID %in% df1$ID,])
     #droplevels could be skipped by using unique(as.character(filteredtablevalues$ID)) in the sapply, not sure what would be the quickest 
     sapply(levels(filteredtablevalues$ID),function(id,tabval)
            {
            sum(tabval$Price[tabval$ID == id])
        },tabval=filteredtablevalues)
 }

如您所见,我添加了两个参数,允许您选择配对颜色/类型。你可以添加这个:

 tmp=getPrices(df1,df2)
 finaltable=cbind.data.frame(ID=names(tmp),Price=tmp)

如果您绝对需要具有列ID和列价格的数据框。

我会在有空的时候尝试一些基准测试,但是以这种方式编写,您应该能够轻松地将其与library(parallel)library(Rmpi)并行化,如果您拥有非常大的数据集,这可以挽救您的生命

编辑:

基准:

我无法重现@denis提出的dplyr示例,但我可以比较data.table版本:

#Create a bigger dataset
nt=10000 #nt as big as you want
df2=rbind.data.frame(df2,
                     list(ID= sample(c("A","B","C"),nt,replace=T),
                          Color=sample(c("Blue","Green"),nt,replace=T),
                          Type=sample.int(5,nt,replace=T),
                          Price=sample.int(5,nt,replace=T)
                          )
                     )

您可以使用library(microbenchmark)

进行基准测试
library(microbenchmark)
microbenchmark(sply=getPrices(df1,df2),dtbl=setDT(df2)[ID %in% unique(df1$ID), .(sum = sum(Price[ Type == 2 & Color == "Blue"])),by = ID],dplyr=df2 %>%  filter(ID %in% unique(df1$ID)) %>%  group_by(ID) %>%  summarize(sum = sum(Price[Type==2 & Color=="Blue"])))

在我的电脑上,它给出了:

Unit: milliseconds
  expr      min       lq      mean    median        uq      max neval
  sply 78.37484 83.89856  97.75373  89.17033 118.96890 131.3226   100
  dtbl 75.67642 83.44380  93.16893  85.65810  91.98584 137.2851   100
 dplyr 90.67084 97.58653 114.24094 102.60008 136.34742 150.6235   100

EDIT2:

sapply似乎比data.table方法略快,但并不显着。但是使用sapply对于拥有庞大的ID表非常有帮助。然后使用library(parallel)并获得更多时间。

现在data.table方法似乎是最快的。但是,sapply的优势在于您可以轻松地并行化它。虽然在这种情况下并且考虑到我如何编写函数getPrices,但只有当您的ID表格很大时它才会有效。