2015-07-29 3 views
0

и заблаговременно за поиск.Подмножество через рамки данных Использование dplyr в R

У меня есть кадр данных событий (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_ID | Period 
---------------------- 
Person1 | 1 
Person1 | 2 
Person1 | 3 
Person2 | 1 
Person2 | 2 
Person2 | 3 
Person3 | 1 
Person3 | 2 
Person3 | 3 

Я хочу, чтобы выяснить, для каждого периода , сколько действия или оценки продолжались в течение периода. Например, если событие Person1 в EV было начальным периодом 5 и конечным периодом 10, то это событие должно появиться в 5,6,7,8,9,10 в PP. Результат будет выглядеть следующим образом:

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 

На данный момент я использую для цикла - что slow.And я сопротивляюсь присоединиться, потому что полный набор данные имеют сотни и тысячи данных. Я попытался с помощью мутировать из пакета dplyr:

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 

Я открыт для использования других пакетов - Я думаю, что я не совсем понимаю, что-то о том, как мутируют работает

+0

@Arun Извинения, исходные данные усечены, поэтому теперь должны быть последовательными. Спасибо, что посмотрели! – user988029

ответ

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 к данные.table на месте (по ссылке).

  • setDT(df2)[df1, on = "Person_ID"] выполняет присоединиться к операцию на колонке Person_ID. Для каждой строки в df1 вычисляются соответствующие совпадающие строки в df2, и все столбцы, соответствующие этим сопоставимым строкам, извлекаются.

  • setDT(df2)[df1, on = "Person_ID", nomatch = 0L], как вы, возможно, догадались только возвращает соответствующие строки, и выходит из этих рядов Person_ID в df1 где нет матча в df2.

  • Часть by = .EACHI - весьма полезный и мощный аргумент.Это помогает вычислить выражение, которое мы предоставляем в j, второй аргумент в пределах [], для каждой строки в df1.

    Например, рассмотрите второй ряд df1. Присоединение к Person_ID, оно соответствует строкам 1,2,3 из df2. И by = .EACHI выполнит выражение, указанное в пределах .(), которое вернет Period = 1,2,3, Event_Type = "Activity" и isBetween = FALSE,TRUE,TRUE. Event_Type перерабатывается, чтобы соответствовать длине самого длинного вектора (= 3).

    Essentially, we are joining and computing at the same time. This is a feature (only?) in data.table, where joins are considered as extensions of subset operations. Since we can compute while subsetting and grouping, we can do exactly the same while joining as well. This is both fastand *memory efficient as the entire join doesn't have to be materialised.

    Чтобы понять это лучше, попробуйте вычисления, что j выражение приведет к последней строке.

    Затем посмотрите на ans, и результат должен быть очевиден.

  • Тогда мы в последний шаг, чтобы сделать и это подсчитать количество Activity и Assessment для каждого Person_ID, Period и иметь их в виде отдельных столбцов. Это можно сделать за один шаг, используя функцию dcast.

    формула означает, что для каждого Person_ID, Period, мы хотели бы sum() значения из inBetween, как отдельный столбец для каждого уникального значения Event_Type.

-1

Вот возможное решение:

  1. Соединение слева PP и EV (dplyr :: left_join) на Person_I D и Период
  2. Группа по Person и период dplyr :: group_by (person_id, период)
  3. Подсчитайте число значений с помощью dplyr :: обобщать()
+0

Спасибо за ваш ответ! Я не уверен, как это будет работать для текущей деятельности. Например. если действие длится с 5 по 10, оно должно появиться в ПП для человеческих месяцев 5,6,7,8,9,10. – user988029

+0

Еще раз спасибо за ваше решение! Есть ли другой способ, который не использует соединение? Таблица содержит c. 200 000 строк каждый, что дает существенный размер кадра данных, который будет трудно обрабатывать. Спасибо! – user988029

+0

Fyi, для шагов 2 и 3 вы можете рассчитывать с помощью 'count' или' group_by' + 'tally' в цепочке dplyr. – Frank

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