Цикл с несколькими функциями subset () в R: есть ли более элегантный / более быстрый способ? - PullRequest
1 голос
/ 13 марта 2020

Я пытаюсь вычислить некоторые исторические c средние значения для достаточно большого фрейма данных (размер 50 000rx 150 c), и для этого мне сначала нужно подгруппировать данные в соответствии с двумя критериями, а затем применить некоторую арифметику c функция для одного из подмножеств столбцов. Затем я записываю результат в другой фрейм данных в виде нового столбца подсчетов / средних / взвешенных средних за определенные периоды. Из-за размера наборов данных (и ограничений моего навыка кодирования) это занимает много времени, и мой вопрос: есть ли лучший способ решения этой проблемы в R?

Вот простой пример, чтобы проиллюстрировать, что я пытаюсь сделать:

library(lubridate)

###  Create dataframe Df

date <- c("01/01/2020", "02/01/2020", "02/01/2020","02/01/2020", "03/01/2020", 
          "03/01/2020", "03/01/2020", "03/01/2020", "04/01/2020", "04/01/2020")
date <- dmy(date)
name <- c("john", "paul", "john", "peter", "peter", 
          "john", "andrew", "john", "peter", "peter")
visits <- c(1, 3, 2, 1, 3, 
            4, 6, 1 ,1, 9)
Df <- data.frame(date, name, visits)
Df


###  Create dataframe Df1

date1 <- c("01/01/2020", "02/01/2020", "03/01/2020", "04/01/2020")
date1 <- dmy(date1)
name1 <- c("john", "paul", "andrew", "peter")
totvisits <- c(0, 0, 0, 0)
Df1 <- data.frame(date1, name1, totvisits)
Df1

Df$name <- as.character(Df$name)
Df1$name1 <- as.character(Df1$name1)

В этом примере я хочу (для каждой пары имя строки1 / дата1 в Df1) подставить Df в соответствии с датой / именем и вернуть количество посещений каждое «имя1» указывалось перед каждым значением «дата1» ie путем суммирования столбца «посещения» для даты <дата1. Затем я хочу сохранить это значение в соответствующей строке нового столбца (в этом примере «Df1 $ totvisits»). Насколько я могу судить, для этого требуется какой-то l oop, что в больших наборах данных неуклюже и занимает много времени. Я тоже попробовал mapply (), но это не так быстро, и это не ускользает от того факта, что мое решение не очень элегантно. </p>

### loop 
for (i in 1:dim(Df1)[1]) {
  Df1[i, 3] <- sum(subset(Df, Df$name == Df1$name1[i] & Df$date <= Df1$date1[i])[,3])
}
Df1

### apply()
f <- function(x, y) {
  sum(subset(Df, (Df$name == x) & (Df$date <= y))[,3])
  }
Df1[, 3] <- mapply(f, x = Df1$name1, y = Df1$date1)
Df1

Любая идея или указатели относительно альтернативного подхода были бы намного признателен.

РЕДАКТИРОВАТЬ:

Чтобы прояснить вышесказанное, я пытаюсь добавить новый столбец в Df1 ('Df1 $ totvisits'), где каждая запись является результатом поиска date1 / name1 в другом фрейме данных ('Df) и возврата суммы посещений, которые произошли до' date1 'для каждого' name1 '. Например, в строке «2020-01-02 paul» в Df1 мне нужно получить значения посещений из Df, где «name == paul» и «date <= 02/01/2020», а затем поместить сумму из них в третьем столбце, второй ряд Df1. Поскольку в Df1 есть только один экземпляр paul с датой <= "2020-01-02", эта запись становится = 3. </p>

Я хотел бы иметь возможность расширить это до поиска значения из диапазона дат, например, даты между x и y, когда человек z посетил. Я хотел бы сделать это, чтобы я мог рассчитывать посещения в n-дневном окне.

В контексте dplyr, я думаю, что я пытаюсь сделать, это 'mutate' Df1 с новым столбцом, который содержит выражение, которое возвращает значения сумм Df $ посещений до (/ между) каждой даты. Просто я не могу заставить его работать, и это сводит меня с ума.

Заранее спасибо за любую помощь с этим.

1 Ответ

1 голос
/ 13 марта 2020

Все еще не уверен на 100%, понимаю ли я, что вы хотите сделать, но вот dplyr способ получить:

В этом примере я хочу (для каждого имени строки1 / даты1 пара в Df1) подмножество Df в соответствии с датой / именем и возвращает количество посещений, которое каждое «имя1» совершило до каждого значения «date1» ie, суммируя столбец «посещения» для даты

library(dplyr)
Df %>% 
  group_by(name) %>% 
  arrange(date) %>% 
  mutate(total = cumsum(visits)) %>% 
  ungroup() %>% 
  arrange(name, date)

Приведенный выше код возвращает:

# A tibble: 10 x 4
   date       name   visits total
   <date>     <fct>   <dbl> <dbl>
 1 2020-01-03 andrew      6     6
 2 2020-01-01 john        1     1
 3 2020-01-02 john        2     3
 4 2020-01-03 john        4     7
 5 2020-01-03 john        1     8
 6 2020-01-02 paul        3     3
 7 2020-01-02 peter       1     1
 8 2020-01-03 peter       3     4
 9 2020-01-04 peter       1     5
10 2020-01-04 peter       9    14

И вот так может выглядеть та же задача с data.table:

library(data.table)
Df <- data.table(Df)
Df[order(date), total:=cumsum(visits), name]

Мы сортируем его, чтобы получить ту же структуру, что и решение выше:

Df[order(name, date),]
          date   name visits total
 1: 2020-01-03 andrew      6     6
 2: 2020-01-01   john      1     1
 3: 2020-01-02   john      2     3
 4: 2020-01-03   john      4     7
 5: 2020-01-03   john      1     8
 6: 2020-01-02   paul      3     3
 7: 2020-01-02  peter      1     1
 8: 2020-01-03  peter      3     4
 9: 2020-01-04  peter      1     5
10: 2020-01-04  peter      9    14
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...