R - 跨不同长度数据帧的多个标准总和
R - Multiple criteria sum across different length data frames
第一个 post,长期用户。
我正在尝试根据 2 个标准对不同长度的另一个数据框中的每个 ID 的列进行有效求和。下面是一个例子:
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
中的函数可能是最好的,但我无法让它们工作。
我稍微更改了您的数据示例,以便它考虑到并非所有 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)
data.frame 的两个主要软件包是 dplyr
和 data.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
一个sapply
版本。它可能存在更优雅的编写方式,但如果你有像你所说的大 tables,你可以轻松地将它并行化。
使用@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)
}
如您所见,我添加了两个参数,允许您 select 对 color/type。你可以添加这个:
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
编辑2:
sapply
似乎比 data.table
方法稍微快一些,但并不显着。但是使用 sapply
对你有巨大的 ID
table 可能真的很有帮助。然后你使用 library(parallel)
并获得更多时间。
现在 data.table
方法似乎是最快的。但是,sapply
的优势在于您可以轻松地将其并行化。尽管在那种情况下,考虑到我编写函数 getPrices
的方式,只有当您的 ID
table 很大时,它才会有效。
第一个 post,长期用户。
我正在尝试根据 2 个标准对不同长度的另一个数据框中的每个 ID 的列进行有效求和。下面是一个例子:
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
中的函数可能是最好的,但我无法让它们工作。
我稍微更改了您的数据示例,以便它考虑到并非所有 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)
data.frame 的两个主要软件包是 dplyr
和 data.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
一个sapply
版本。它可能存在更优雅的编写方式,但如果你有像你所说的大 tables,你可以轻松地将它并行化。
使用@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)
}
如您所见,我添加了两个参数,允许您 select 对 color/type。你可以添加这个:
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
编辑2:
sapply
似乎比 data.table
方法稍微快一些,但并不显着。但是使用 sapply
对你有巨大的 ID
table 可能真的很有帮助。然后你使用 library(parallel)
并获得更多时间。
现在 data.table
方法似乎是最快的。但是,sapply
的优势在于您可以轻松地将其并行化。尽管在那种情况下,考虑到我编写函数 getPrices
的方式,只有当您的 ID
table 很大时,它才会有效。