Как R способ сделать следующую группу? - PullRequest
8 голосов
/ 16 июня 2011

У меня есть такой набор данных:

# date     # value    class
1984-04-01 95.32384   A
1984-04-01 39.86818   B
1984-07-01 43.57983   A
1984-07-01 10.83754   B

Теперь я хотел бы сгруппировать данные по данным и вычесть значение класса B из класса A. Я посмотрел в ddply, суммировать, таять и агрегировать, но не могу получить то, что хочу. Есть ли способ сделать это легко? Обратите внимание, что у меня есть ровно два значения на дату, одно из класса A и одно из класса B. Я имею в виду, я мог бы перестроить его в два dfs, упорядочить по дате и классу и снова объединить, но я чувствую, что есть более R-способ сделать это.

Ответы [ 4 ]

7 голосов
/ 16 июня 2011

Предполагая, что этот фрейм данных (сгенерированный как в посте Прасада, но с set.seed для воспроизводимости):

set.seed(123)
DF <- data.frame( date = rep(seq(as.Date('1984-04-01'), 
                                 as.Date('1984-04-01') + 3, by=1), 
                            1, each=2),
                  class = rep(c('A','B'), 4),
                  value = sample(1:8))

, тогда мы рассмотрим семь решений:

1) zoo может дать нам однострочное решение (не считая оператора library):

library(zoo)
z <- with(read.zoo(DF, split = 2), A - B)

с указанием этой zoo серии:

> z
1984-04-01 1984-04-02 1984-04-03 1984-04-04 
        -3          3          3         -5 

Также обратите внимание, что as.data.frame(z) или data.frame(time = time(z), value = coredata(z)) дает фрейм данных;однако вы можете оставить его в качестве объекта зоопарка, поскольку это временной ряд, и другие операции с ним удобнее выполнять в этой форме, например plot(z)

2) sqldf может также дать решение с одним оператором (кроме вызова library):

> library(sqldf)
> sqldf("select date, sum(((class = 'A') - (class = 'B')) * value) as value
+ from DF group by date")
        date value
1 1984-04-01    -3
2 1984-04-02     3
3 1984-04-03     3
4 1984-04-04    -5

3) tapply может использоваться как основа решения, основанного на решении sqldf:

> with(DF, tapply(((class =="A") - (class == "B")) * value, date, sum))
1984-04-01 1984-04-02 1984-04-03 1984-04-04 
        -3          3          3         -5 

4) агрегат можно использовать так же, как sqldf и tapply выше (хотя уже появилось немного другое решение, также основанное на aggregate):

> aggregate(((DF$class=="A") - (DF$class=="B")) * DF["value"], DF["date"], sum)
        date value
1 1984-04-01    -3
2 1984-04-02     3
3 1984-04-03     3
4 1984-04-04    -5

5) summaryBy из пакета doBy может предоставить еще одно решение, хотя для его поддержки требуется transform:

> library(doBy)
> summaryBy(value ~ date, transform(DF, value = ((class == "A") - (class == "B")) * value), FUN = sum, keep.names = TRUE)
        date value
1 1984-04-01    -3
2 1984-04-02     3
3 1984-04-03     3
4 1984-04-04    -5

6) remix из пакета remix может сделать это тоже, но с transform и особенно привлекательным выводом:

> library(remix)
> remix(value ~ date, transform(DF, value = ((class == "A") - (class == "B")) * value), sum)
value ~ date
============

+------+------------+-------+-----+
|                           | sum |
+======+============+=======+=====+
| date | 1984-04-01 | value | -3  |
+      +------------+-------+-----+
|      | 1984-04-02 | value | 3   |
+      +------------+-------+-----+
|      | 1984-04-03 | value | 3   |
+      +------------+-------+-----+
|      | 1984-04-04 | value | -5  |
+------+------------+-------+-----+

7) summary.formula inпакет Hmisc также имеет хороший вывод:

> library(Hmisc)
> summary(value ~ date, data = transform(DF, value = ((class == "A") - (class == "B")) * value), fun = sum, overall = FALSE)
value    N=8

+----+----------+-+-----+
|    |          |N|value|
+----+----------+-+-----+
|date|1984-04-01|2|-3   |
|    |1984-04-02|2| 3   |
|    |1984-04-03|2| 3   |
|    |1984-04-04|2|-5   |
+----+----------+-+-----+
6 голосов
/ 16 июня 2011

Самый простой способ, который я могу придумать, это использовать dcast из пакета reshape2, чтобы создать фрейм данных с одной датой на строку и столбцами A и B, а затем использовать transformdo A-B:

df <- data.frame( date = rep(seq(as.Date('1984-04-01'), 
                                 as.Date('1984-04-01') + 3, by=1), 
                            1, each=2),
                  class = rep(c('A','B'), 4),
                  value = sample(1:8))

require(reshape2)
df_wide <- dcast(df, date  ~ class, value_var = 'value')

> df_wide
        date A B
1 1984-04-01 8 7
2 1984-04-02 6 1
3 1984-04-03 3 4
4 1984-04-04 5 2

> transform( df_wide, A_B = A - B )

        date A B A_B
1 1984-04-01 8 7   1
2 1984-04-02 6 1   5
3 1984-04-03 3 4  -1
4 1984-04-04 5 2   3
5 голосов
/ 16 июня 2011

В базе R я бы подошел к проблеме, используя aggregate и sum.Это работает путем преобразования каждого значения класса B в его отрицательное значение:

(используя данные, предоставленные @PrasadChalasani)

df <- within(df, value[class=="B"] <- -value[class=="B"])
aggregate(df$value, by=list(date=df$date), sum)

        date x
1 1984-04-01 3
2 1984-04-02 2
3 1984-04-03 2
4 1984-04-04 1
4 голосов
/ 16 июня 2011

Для справки, мне больше нравится опция изменения формы. Вот вариант plyr с использованием суммирования:

library(plyr)

ddply(df, "date", summarise
    , A = value[class == "A"]
    , B = value[class == "B"]
    , A_B = value[class == "A"] - value[class == "B"]
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...