使用R中的dplyr在数据帧中进行子集

时间:2015-07-29 09:26:52

标签: r time-series subset dplyr apply

并提前感谢您的回顾。

我有一个事件数据框(EV):

Event_ID | Person_ID | Start_Period | End_Period | Event_Type
------------------------------------------------------------
A        | Person1   | 1            | 9          | Assessment
B        | Person1   | 2            | 9          | Activity
C        | Person1   | 3            | 6          | Assessment
D        | Person2   | 3            | 6          | Activity
E        | Person3   | 7            | 13         | Assessment

我有一个Person-Periods(PP)数据框

Person_ID | Period
----------------------
Person1   | 1
Person1   | 2
Person1   | 3
Person2   | 1
Person2   | 2
Person2   | 3
Person3   | 1
Person3   | 2
Person3   | 3

我想了解每个时期内有多少活动或评估在此期间进行。例如,如果EV中Person1的事件的开始周期为5且结束周期为10,则此事件应显示在PP中的5,6,7,8,9,10中。结果如下:

Person_ID | Period | ActivitiesFreq | AssessmentsFreq
----------------------------------------------
Person1   | 1      | 0              | 1
Person1   | 2      | 1              | 1
Person1   | 3      | 1              | 2
Person2   | 1      | 0              | 0
Person2   | 2      | 0              | 0
Person2   | 3      | 1              | 0
Person3   | 1      | 0              | 0
Person3   | 2      | 0              | 0
Person3   | 3      | 0              | 0

目前我正在使用for循环 - 这很慢。我拒绝加入,因为完整数据集有数百和数千个数据。我尝试过使用dplyr包中的mutate:

mutate(PP,SUM(EV$Person_ID==Person_ID,EV$Start_Period<=Period,EV$End_Period>=Period)

但是我收到以下错误:

Warning messages:
1: In mutate_impl(.data, dots) :
  is.na() applied to non-(list or vector) of type 'NULL'
2: In mutate_impl(.data, dots) :
  longer object length is not a multiple of shorter object length
3: In mutate_impl(.data, dots) :
  longer object length is not a multiple of shorter object length

我愿意使用其他软件包 - 我想我不太了解mutate的工作方式

3 个答案:

答案 0 :(得分:4)

这是使用data.table v1.9.5(当前devel版本)的解决方案。我将它用于新的on=功能,允许连接而无需设置密钥:

require(data.table) # v1.9.5+
ans = setDT(df2)[df1, .(Period, Event_Type, 
                        isBetween = Period %between% c(Start_Period, End_Period)), 
                by = .EACHI, on = "Person_ID", nomatch = 0L]

dcast(ans, Person_ID + Period ~ Event_Type, fun.aggregate = sum)
# Using 'isBetween' as value column. Use 'value.var' to override
#    Person_ID Period Activity Assessment
# 1:   Person1      1        0          1
# 2:   Person1      2        1          1
# 3:   Person1      3        1          2
# 4:   Person2      1        0          0
# 5:   Person2      2        0          0
# 6:   Person2      3        1          0
# 7:   Person3      1        0          0
# 8:   Person3      2        0          0
# 9:   Person3      3        0          0

工作原理:

  • setDT() data.frame 转换为 data.table 就地(通过引用)。

  • setDT(df2)[df1, on = "Person_ID"]对列Person_ID执行加入操作。对于df1中的每一行,计算df2中相应的匹配行,并提取与这些匹配行对应的所有列。

  • setDT(df2)[df1, on = "Person_ID", nomatch = 0L],因为您可能已经猜到只返回匹配的行,并在Person_ID中遗漏了df1中的df2by = .EACHI

  • j部分非常有用且功能强大。它有助于计算我们在[]中提供的表达式,df1中的第二个参数,df1中的每一行。

    例如,考虑Person_ID的第2行。加入df2,它与by = .EACHI的第1,2,3行匹配。 .()将执行Period = 1,2,3中提供的表达式,该表达式将返回Event_Type = "Activity"isBetween = FALSE,TRUE,TRUEEvent_Typej被回收以适应最长矢量的长度(= 3)。

      

    基本上,我们同时加入计算。这是 data.table 中的一项功能(仅限?),其中 join 被视为 subset 操作的扩展。由于我们可以在进行子集化和分组时进行计算,因此我们也可以在加入时完全相同。这是 fast *内存效率,因为整个连接不必实现。

    为了更好地理解它,请尝试计算最后一行将导致ans表达式。

    然后,看看Activity,结果应该是显而易见的。

  • 然后我们要采取最后一步,即为每个Assessment计算Person_ID, Perioddcast的数量,并将它们作为单独的列。这可以使用Person_ID, Period函数一步完成。

    该公式暗示,对于每个sum(),我们希望inBetween Event_Type的值setTimeout( function () { $('#ddlSessionList option:contains('+sessionName()+')') .prop('selected', true); }, 200); ,作为单独的列,用于{{1}的每个唯一值}。

答案 1 :(得分:0)

如果没有加入数据集,我还没有想出办法。这是一个基于 dplyr 的解决方案,首先使用left_join连接数据集(我只从该任务所需的EV中获取了三列)。

加入数据集后,您只需按Person_ID对数据集进行分组,并计算两种类型事件的累计总和。我投了arrange,以防Period Person_ID内的Event_Type内的真实数据集不正常,并删除了mutate中的library(dplyr) PP %>% left_join(., select(EV, -Event_ID, -End_Period), by = c("Person_ID", "Period" = "Start_Period")) %>% group_by(Person_ID) %>% arrange(Period) %>% mutate(ActivitiesFreq = cumsum(Event_Type == "Activity" & !is.na(Event_Type)), AssessmentFreq = cumsum(Event_Type == "Assessment" & !is.na(Event_Type)), Event_Type = NULL) Source: local data frame [9 x 4] Groups: Person_ID Person_ID Period ActivitiesFreq AssessmentFreq 1 Person1 1 0 1 2 Person1 2 1 1 3 Person1 3 1 2 4 Person2 1 0 0 5 Person2 2 0 0 6 Person2 3 1 0 7 Person3 1 0 0 8 Person3 2 0 0 9 Person3 3 0 0 列。

AVERAGE(cell1,cell2...)

答案 2 :(得分:-1)

这是一个潜在的解决方案:

  1. 在Person_ID和Period
  2. 上加入PP和EV(dplyr :: left_join)
  3. 按人员和期间分组dplyr :: group_by(Person_ID,Period)
  4. 使用dplyr :: summarize()
  5. 计算值的数量