2009-11-02 1 views
207

Предположим, у меня есть два столбца данных. Первый содержит такие категории, как «Первый», «Второй», «Третий» и т. Д. Во втором есть числа, которые представляют количество раз, когда я видел «Первое».Как суммировать переменную по группе?

Например:

Category  Frequency 
First  10 
First  15 
First  5 
Second  2 
Third  14 
Third  20 
Second  3 

Я хочу, чтобы отсортировать данные по категориям и просуммировать Частоты:

Category  Frequency 
First  30 
Second  5 
Third  34 

Как бы я сделать это в R?

ответ

234

Использование aggregate:

aggregate(x$Frequency, by=list(Category=x$Category), FUN=sum) 
    Category x 
1 First 30 
2 Second 5 
3 Third 34 

(вложение @thelatemail комментарий), aggregate имеет формулу интерфейс слишком

aggregate(Frequency ~ Category, x, sum) 

Или, если вы хотите, чтобы объединить несколько столбцов, вы можете использовать . обозначение (работы для одной колонки тоже)

aggregate(. ~ Category, x, sum) 

или tapply:

tapply(x$Frequency, x$Category, FUN=sum) 
First Second Third 
    30  5  34 

Используя эти данные:

x <- data.frame(Category=factor(c("First", "First", "First", "Second", 
             "Third", "Third", "Second")), 
        Frequency=c(10,15,5,2,14,20,3)) 
+2

@AndrewMcKinlay, R использует тильду для определения символических формул, для статистики и других функций. Его можно интерпретировать как * «Модель по категориям» * или * «Частота в зависимости от категории» *.Не все языки используют специальный оператор для определения символической функции, как это сделано в R здесь. Возможно, с этой «интерпретацией на естественном языке» оператора тильды он становится более значимым (и даже интуитивным). Я лично считаю это символическое представление формул лучше, чем некоторые из более подробных альтернатив. – r2evans

13

Если x является dataframe с вашими данными, то следующий будет делать то, что вы хотите:

require(reshape) 
recast(x, Category ~ ., fun.aggregate=sum) 
19
library(plyr) 
ddply(tbl, .(Category), summarise, sum = sum(Frequency)) 
15

Просто добавить третий вариант:

require(doBy) 
summaryBy(Frequency~Category, data=yourdataframe, FUN=sum) 

EDIT: это очень старый ответ. Теперь я бы рекомендовал использовать group_by и суммировать из dplyr, как в ответе @docendo.

30

Это несколько related to this question.

Вы также можете просто использовать по() функции:

x2 <- by(x$Frequency, x$Category, sum) 
do.call(rbind,as.list(x2)) 

Эти другие пакеты (plyr, перекроить) имеют преимущество возвращения data.frame, но это стоит быть знакомым с помощью (), так как это базовая функция.

48

Ответ, предоставленный rcs, работает и прост. Тем не менее, если вы регулируете большие массивы данных и необходимо повышение производительности есть более быстрый вариант:

library(data.table) 
data = data.table(Category=c("First","First","First","Second","Third", "Third", "Second"), 
        Frequency=c(10,15,5,2,14,20,3)) 
data[, sum(Frequency), by = Category] 
# Category V1 
# 1: First 30 
# 2: Second 5 
# 3: Third 34 
system.time(data[, sum(Frequency), by = Category]) 
# user system elapsed 
# 0.008  0.001  0.009 

Давайте сравним, что то же самое, используя данные.рама и выше выше:

data = data.frame(Category=c("First","First","First","Second","Third", "Third", "Second"), 
        Frequency=c(10,15,5,2,14,20,3)) 
system.time(aggregate(data$Frequency, by=list(Category=data$Category), FUN=sum)) 
# user system elapsed 
# 0.008  0.000  0.015 

И если вы хотите сохранить столбец это синтаксис:

data[,list(Frequency=sum(Frequency)),by=Category] 
# Category Frequency 
# 1: First  30 
# 2: Second   5 
# 3: Third  34 

Разница становится более заметным с большими наборами данных, так как ниже код демонстрирует:

data = data.table(Category=rep(c("First", "Second", "Third"), 100000), 
        Frequency=rnorm(100000)) 
system.time(data[,sum(Frequency),by=Category]) 
# user system elapsed 
# 0.055  0.004  0.059 
data = data.frame(Category=rep(c("First", "Second", "Third"), 100000), 
        Frequency=rnorm(100000)) 
system.time(aggregate(data$Frequency, by=list(Category=data$Category), FUN=sum)) 
# user system elapsed 
# 0.287  0.010  0.296 

Для нескольких агрегатов, вы можете комбинировать lapply и .SD следующим

data[, lapply(.SD, sum), by = Category] 
# Category Frequency 
# 1: First  30 
# 2: Second   5 
# 3: Third  34 
+7

+1 Но 0.296 против 0.059 не особенно впечатляет. Размер данных должен быть намного больше, чем 300 тыс. Строк, и с более чем 3 группами, для того, чтобы data.table отображался. Например, мы попытаемся поддерживать более 2 миллиардов строк, так как некоторые пользователи data.table имеют 250 ГБ оперативной памяти, а GNU R теперь поддерживает длину> 2^31. –

+1

Правда. Оказывается, у меня нет всей этой ОЗУ, и она просто пыталась предоставить некоторые доказательства превосходной производительности data.table. Я уверен, что разница будет еще больше с большим количеством данных. – asieira

+0

У меня было 7 мил наблюдений dplyr заняло 0,3 секунды, а агрегат() занял 22 секунды, чтобы завершить операцию. Я собирался опубликовать его на эту тему, и вы избили меня! – zazu

114

Совсем недавно, вы можете также использовать dplyr пакет для этой цели:

library(dplyr) 
x %>% 
    group_by(Category) %>% 
    summarise(Frequency = sum(Frequency)) 

#Source: local data frame [3 x 2] 
# 
# Category Frequency 
#1 First  30 
#2 Second   5 
#3 Third  34 

Или, для нескольких сводных колонн (работает с одной колонкой тоже):

x %>% 
    group_by(Category) %>% 
    summarise_each(funs(sum)) 

Обновление для dplyr> = 0.5:summarise_each был заменен на summarise_all, summarise_at и summarise_if Семейство функций в dplyr.

Или, если у вас есть несколько столбцов в группе, вы можете указать их все в group_by разделенных запятыми:

mtcars %>% 
    group_by(cyl, gear) %>%       # multiple group columns 
    summarise(max_hp = max(hp), mean_mpg = mean(mpg)) # multiple summary columns 

Для получения дополнительной информации, в том числе оператора %>% см introduction to dplyr.

+0

Насколько быстро это происходит по сравнению с таблицами data.table и совокупными альтернативами, представленными в других ответах? – asieira

+2

@asieira, который является самым быстрым и насколько велика разница (или если разница заметна) всегда будет зависеть от вашего размера данных. Как правило, для больших наборов данных, например, некоторых GB, data.table, скорее всего, будет быстрее всего. При меньших размерах данных, data.table и dplyr часто близки, также в зависимости от количества групп. Однако данные, таблица и dplyr будут намного быстрее, чем базовые функции, но могут быть в 100-1000 раз быстрее для некоторых операций. Также см. [Здесь] (http://stackoverflow.com/questions/21435339/data-table-vs-dplyr-can-one-do-something-well-the-other-cant-or-does-poorly) –

15

Несколько лет спустя, просто добавить еще один простой базы R решение, которое не присутствует здесь в течение некоторого Reason-xtabs

xtabs(Frequency ~ Category, df) 
# Category 
# First Second Third 
# 30  5  34 

Или, если хотите data.frame назад

as.data.frame(xtabs(Frequency ~ Category, df)) 
# Category Freq 
# 1 First 30 
# 2 Second 5 
# 3 Third 34 
14

Хотя у меня есть недавно стал конвертировать в dplyr для большинства этих типов операций, пакет sqldf по-прежнему очень приятный (и IMHO более читабельный) для некоторых вещей.

Вот пример того, как этот вопрос может быть решен с sqldf

x <- data.frame(Category=factor(c("First", "First", "First", "Second", 
            "Third", "Third", "Second")), 
       Frequency=c(10,15,5,2,14,20,3)) 

sqldf("select 
      Category 
      ,sum(Frequency) as Frequency 
     from x 
     group by 
      Category") 

## Category Frequency 
## 1 First  30 
## 2 Second   5 
## 3 Third  34 
0

использованием cast вместо recast (обратите внимание 'Frequency' теперь 'value')

df <- data.frame(Category = c("First","First","First","Second","Third","Third","Second") 
        , value = c(10,15,5,2,14,20,3)) 

install.packages("reshape") 

result<-cast(df, Category ~ . ,fun.aggregate=sum) 

получить:

Category (all) 
First  30 
Second 5 
Third  34