Расчет промежуточных итогов в R - PullRequest
8 голосов
/ 05 ноября 2010

У меня есть фрейм данных с 900 000 строк и 11 столбцов в R. Имена и типы столбцов следующие:

column name: date / mcode / mname / ycode / yname / yissue  / bsent   / breturn / tsent   / treturn / csales
type:        Date / Char  / Char  / Char  / Char  / Numeric / Numeric / Numeric / Numeric / Numeric / Numeric

Я хочу рассчитать промежуточные итоги. Например, я хочу вычислять суммы при каждом изменении yname и добавлять промежуточные итоги ко всем числовым переменным. Существует 160 различных y-имен, поэтому в итоговой таблице должен быть указан промежуточный итог каждого y-имени. Я еще не отсортировал данные, но это не проблема, потому что я могу отсортировать данные любым способом, каким захочу. Ниже приведен фрагмент из моих данных:

             date     mcode mname            ycode    yname   yissue bsent breturn tsent treturn csales
417572 2010-07-28     45740 ENDPOINT A        5772    XMAG  20100800     7       0     7       0      0
417573 2010-07-31     45740 ENDPOINT A        5772    XMAG  20100800     0       0     0       0      1
417574 2010-08-04     45740 ENDPOINT A        5772    XMAG  20100800     0       0     0       0      1
417575 2010-08-14     45740 ENDPOINT A        5772    XMAG  20100800     0       0     0       0      1
417576 2010-08-26     45740 ENDPOINT A        5772    XMAG  20100800     0       4     0       0      0
417577 2010-07-28     45741 ENDPOINT L        5772    XMAG  20100800     2       0     2       0      0
417578 2010-08-04     45741 ENDPOINT L        5772    XMAG  20100800     2       0     2       0      0
417579 2010-08-26     45741 ENDPOINT L        5772    XMAG  20100800     0       4     0       0      0
417580 2010-07-28     46390 ENDPOINT R        5772    XMAG  20100800     3       0     3       0      1
417581 2010-07-29     46390 ENDPOINT R        5772    XMAG  20100800     0       0     0       0      2
417582 2010-08-01     46390 ENDPOINT R        5779    YMAG  20100800     3       0     3       0      0
417583 2010-08-11     46390 ENDPOINT R        5779    YMAG  20100800     0       0     0       0      1
417584 2010-08-20     46390 ENDPOINT R        5779    YMAG  20100800     0       0     0       0      1
417585 2010-08-24     46390 ENDPOINT R        5779    YMAG  20100800     2       0     2       0      1
417586 2010-08-26     46390 ENDPOINT R        5779    YMAG  20100800     0       2     0       2      0
417587 2010-07-28     46411 ENDPOINT D        5779    YMAG  20100800     6       0     6       0      0
417588 2010-08-08     46411 ENDPOINT D        5779    YMAG  20100800     0       0     0       0      1
417589 2010-08-11     46411 ENDPOINT D        5779    YMAG  20100800     0       0     0       0      1
417590 2010-08-26     46411 ENDPOINT D        5779    YMAG  20100800     0       4     0       4      0

Какую функцию я должен использовать здесь? Может быть, что-то вроде SQL group by?

Ответы [ 6 ]

10 голосов
/ 05 ноября 2010

OK. Предполагая, что ваши данные находятся во фрейме данных с именем foo:

> head(foo)
             date mcode      mname ycode yname   yissue bsent breturn tsent
417572 2010/07/28 45740 ENDPOINT A  5772  XMAG 20100800     7       0     7
417573 2010/07/31 45740 ENDPOINT A  5772  XMAG 20100800     0       0     0
417574 2010/08/04 45740 ENDPOINT A  5772  XMAG 20100800     0       0     0
417575 2010/08/14 45740 ENDPOINT A  5772  XMAG 20100800     0       0     0
417576 2010/08/26 45740 ENDPOINT A  5772  XMAG 20100800     0       4     0
417577 2010/07/28 45741 ENDPOINT L  5772  XMAG 20100800     2       0     2
       treturn csales
417572       0      0
417573       0      1
417574       0      1
417575       0      1
417576       0      0
417577       0      0

Затем будет произведена агрегация числовых столбцов в ваших данных:

> aggregate(cbind(bsent, breturn, tsent, treturn, csales) ~ yname, data = foo, 
+           FUN = sum)
  yname bsent breturn tsent treturn csales
1  XMAG    14       8    14       0      6
2  YMAG    11       6    11       6      5

Это было использование фрагмента данных, которые вы включили в свой Q. Я использовал интерфейс формулы для aggregate(), что немного лучше в данном случае, потому что вам не нужны все биты foo$ в именах переменных Вы хотите совокупность. Если в вашем полном наборе данных отсутствуют данные (NA), вам нужно добавить дополнительный аргумент na.rm = TRUE, который будет передан в sum(), например:

> aggregate(cbind(bsent, breturn, tsent, treturn, csales) ~ yname, data = foo, 
+           FUN = sum, na.rm = TRUE)
4 голосов
/ 05 ноября 2010

Вы также можете использовать xtabs или tapply:

xtabs(cbind(bsent, breturn, tsent, treturn, csales) ~ yname, data)

tapply(data$bsent, data$yname, sum)
4 голосов
/ 05 ноября 2010

Или библиотека plyr, которая легко расширяема для других классов данных:

> library(plyr)
> result.2 <- ddply(df$a, .(df$b), sum)
> result.2
  df.b V1
1 down 30
2   up 25
3 голосов
/ 05 ноября 2010

если ваши данные большие и скорость имеет значение, я бы порекомендовал использовать R-строку функции R, которая намного быстрее.я применил 3 метода (f1 = aggregate, f2 = ddply, f3 = tapply), предложенных в ответах, чтобы сравнить его с f4 = rowum, и вот что я нахожу:

   test replications elapsed relative
4 f4()          100   0.033     1.00
3 f3()          100   0.046     1.39
1 f1()          100   0.165     5.00
2 f2()          100   0.605    18.33

я добавил свой кодниже, если кто-то хочет исследовать более подробно.

library(plyr);
library(rbenchmark);

val  = rnorm(50);
name = rep(letters[1:5], each = 10);
data = data.frame(val, name);

f1 = function(){aggregate(data$val, by=list(data$name), FUN=sum)}
f2 = function(){ddply(data, .(name), summarise, sum = sum(val))}
f3 = function(){tapply(data$val, data$name, sum)}
f4 = function(){rowsum(x = data$val, group = data$name)}

benchmark(f1(), f2(), f3(), f4(),
          columns=c("test", "replications", "elapsed", "relative"),
          order="relative", replications=100)
2 голосов
/ 05 ноября 2010

Существует пакет R под названием sqldf, который позволяет вам использовать команды SQL на R data.frames.Кроме того, как вы уже сказали, было бы неплохо GROUP BY.Вы можете легко хранить свои данные в локальной базе данных MySQL и подключаться к R с помощью пакета RMySQL (вы также можете использовать большинство других СУБД, но MySQL проще всего настроить).

Насколько я могу судить, plyr - отличный пакет.Но из того, как вы спрашиваете и сравниваете свою проблему с GROUP BY, я полагаю, вы знаете кое-что о SQL, поэтому вам будет проще его использовать.Есть удобные функции, такие как dbReadTable, плюс, если ваши данные растут больше, вы можете выбрать только части данных, чтобы анализ выполнялся только с тем, что вам действительно нужно.

2 голосов
/ 05 ноября 2010

Вы можете использовать aggregate

Например, скажите, что у вас есть

val = rnorm(50)
name = rep(letters[1:5], each=10)
data <- data.frame(val, name)

Тогда вы можете сделать

aggregate(data$val, by=list(data$name), FUN=sum)
...