我有一个非常大的数据框(~800K 行),按 CustomerID、最早事务 {"日期"} 和 Sales 排序,如下所示......
CustomerID Date Sales
AAA123 1-01-2015 49.00
AAA123 1-02-2015 24.00
AAA123 1-03-2015 17.00
BBB456 2-01-2015 117.00
CCC789 1-01-2012 65.20
CCC789 12-12-2012 43.00
我正在尝试有条件地聚合此 data.frame,这样我只想为每个显示他/她最早重复交易的"客户"获取一行(即,如果他们有多个行用于该"CustomerID",则为上表中的第二笔交易),除非该"客户"只有一笔交易, 在这种情况下,我希望"客户"的唯一交易在我的结果中显示为他们的"最早"交易条目。所以从本质上讲,我生成的 data.frame 看起来像这样:
CustomerID Date Sales
AAA123 1-01-2015 49.00
BBB456 2-01-2015 117.00
CCC789 1-01-2012 65.20
我试过使用
results <- do.call(rbind,by(old_data,old_data$CustomerID,function(x) x[-1,]))
但不幸的是,我无法让它按照我想要的方式工作。相反,它会删除那些只有一笔交易的"客户"。有谁知道一种方法可以有条件地应用像"do.call"这样快速、高效且易于应用的函数?
对于大型数据集,data.table
可以有效地使用。 我们将"data.frame"转换为"data.table"(setDT(df1)
),按"CustomerID"分组,我们得到最小"日期"的索引并子集数据集行。
library(data.table)
library(lubridate)
setDT(df1)[, .SD[which.min(mdy(Date))] , by = CustomerID]
# CustomerID Date Sales
# 1: AAA123 1-01-2015 49.0
# 2: BBB456 2-01-2015 117.0
# 3: CCC789 1-01-2012 65.2
或者我们在与"CustomerID"分组后按"日期"order
,然后得到Data.table子集的第一个元素(.SD
)。
setDT(df1)[order(mdy(Date)), head(.SD, 1L) , by = CustomerID]
以上是根据OP显示的预期产出计算的。 但是,根据描述,这是需要的第二笔交易,在这种情况下,我们可能需要一个条件
setDT(df1)[order(mdy(Date)), if(.N==1) .SD else .SD[2L],
by = CustomerID]
试试这个(其中"df"是数据框的名称):
df <- df[order(df$CustomerID, df$Date),]
df <- df[!duplicated(df$CustomerID),]
第一行按 CustomerID 对数据框进行排序,然后按日期顺序对每个客户的交易进行排序。 order() 函数的默认值是按递增顺序排序,以便每个客户的交易将按最早到最新的顺序列出。
第二行删除具有重复 CustomerID 的所有行 - 留下每个客户的第一个(或唯一)事务。