Условное суммирование (R) - PullRequest
       33

Условное суммирование (R)

4 голосов
/ 06 декабря 2010

Я пытаюсь создать условную сумму, чтобы вычислить среднее значение. Идея состоит в том, что функция (или оператор apply) проверяет, является ли определенное значение истинным (например, x> 0), а затем суммирует все значения x, которые выше нуля. Последний шаг - разделить эту сумму на количество экземпляров, которые больше нуля. Поиск условной суммы (мин) не дал мне полезной информации.

Это часть данных:

> tmpData
   Instrument TradeResult.Currency.
1         JPM                    -3
2         JPM                   264
3         JPM                   284
4         JPM                    69
5         JPM                   283
11        KFT                    -8
12        KFT                   -48
13        KFT                   125
14        KFT                  -150
15        KFT                  -206
16        KFT                   107

Из функций, которые я пробовал, наиболее перспективными являются следующие:

avgProfit <- function(x) {
    ifelse(x > 0,
    sum(x) / length(which(x > 0)),
    return(0))
    }

Однако, выход этой функции равен 0:

> with(tmpData, tapply(TradeResult.Currency., Instrument, avgProfit))
JPM KFT 
  0   0     
> avgProfit(tmpData$TradeResult.Currency.)
[1] 0
> x
 [1] 1 1 2 1 2 3 3 3 4 4

(значения должны быть 225 для JPM (всего 900, деленное на 4 экземпляра, которые больше нуля) и 116 для KFT)

Несмотря на то, что я вычисляю сумму x (которая, если я правильно понимаю, должна быть суммой отдельных значений в data.frame) в функции, вывод переменной 'x' озадачивает меня. Я не могу найти, откуда взялись эти 1,2,3 и четверки.

Как рассчитать условную сумму? Кроме того, мне нужно использовать функцию или я делаю ее слишком сложной (возможно, для этого есть встроенная функция R, которую я упустил из виду?)

Любые мысли приветствуются,

С уважением,

Ответы [ 6 ]

9 голосов
/ 06 декабря 2010

вероятно, это простой способ отбросить неиспользуемые строки, а затем объединить их:

aggregate(TradeResult.Currency.~Instrument,
  mean,
  data=subset(tmpData,TradeResult.Currency.>0))
5 голосов
/ 06 декабря 2010

Вы были почти там, я думаю, что если это было неправильное направление, так как вы хотите среднее, а не поэлементное сравнение.

Вы захотите подумать, не встретите ли вы пропущенные значения, чтобы вы могли правильно с этим справиться.

tmpData <- read.table(textConnection("  Instrument TradeResult.Currency.
1         JPM                    -3
2         JPM                   264
3         JPM                   284
4         JPM                    69
5         JPM                   283
11        KFT                    -8
12        KFT                   -48
13        KFT                   125
14        KFT                  -150
15        KFT                  -206
16        KFT                   107"))



with(tmpData, tapply(TradeResult.Currency., Instrument, function(x) mean(x[x > 0])))

JPM KFT 225 116

4 голосов
/ 06 декабря 2010

В последнее время было довольно много таких вопросов по агрегации / условному анализу данных. Всегда интересно увидеть разные подходы. Я думал, что добавлю подход, использующий plyr. Мне нравятся функции plyr, потому что они предоставляют стандартный синтаксис для всех функций и позволяют вам определять структуру как ввода, так и вывода. Здесь мы будем использовать ddply, поскольку мы передаем data.frame и хотим, чтобы data.frame вернулся на другую сторону. Мы используем функцию summarise для вычисления среднего значения для каждого инструмента, где значения положительны.

library(plyr)
ddply(tmpData, .(instrument), summarise, avgProfit = mean(TCurr[TCurr > 0]))

Чтобы продолжить сравнение производительности @Joris, похоже, что ddply также работает, если не лучше, чем другие подходы:

> tmpData <- data.frame(
+      instrument = rep(c("JPM","KFT"),each=10e6),
+      TCurr = runif(20e6,-10,100)
+  )
> 
>  system.time(
+ ddply(tmpData, .(instrument), summarise, avgProfit = mean(TCurr[TCurr > 0]))
+  )
   user  system elapsed 
   4.43    0.89    5.32 
>  
>  avgProfit <- function(x) { mean(x[x>0])}
>  
>  system.time(
+ with(tmpData,tapply(TCurr,instrument,avgProfit))
+ )
   user  system elapsed 
   7.88    0.47    8.36 
>  
> system.time(
+ aggregate(TCurr~instrument,mean,data=subset(tmpData,TCurr>0))
+ )
   user  system elapsed 
  28.29    2.35   30.65 
2 голосов
/ 06 декабря 2010

агрегат - это, пожалуй, самый простой способ, но я не согласен с «уборщиком, потому что вам не нужно писать пользовательскую функцию». Читаемость повышается при определении некоторых четких функций. Особенно, если вам нужно это среднее значение пару раз в ваших скриптах.

Агрегат немного быстрее, чем ваша пользовательская функция, потому что вы забыли об индексах. Вы хотели сделать это:

avgProfit <- function(x){
  mean(x[x>0])
}

Это снова быстрее, чем агрегат, из-за отсутствия служебных данных:

> tmpData <- data.frame(
+     instrument = rep(c("JPM","KFT"),each=10000),
+     TCurr = runif(20000,-10,100)
+ )

> system.time(
+   with(tmpData,tapply(TCurr,instrument,avgProfit)))
   user  system elapsed 
   0.02    0.00    0.02 

> system.time(
+   aggregate(TCurr~instrument,mean,data=subset(tmpData,TCurr>0)))
   user  system elapsed 
   0.09    0.00    0.10 

В большинстве случаев вы можете просто игнорировать эту разницу. На огромных наборах данных (n> 100 000) вы начнете чувствовать это, особенно если вам нужно сделать это для целого набора переменных.

РЕДАКТИРОВАТЬ: только что увидел, что у mdsummer было точно такое же решение, скрытое аккуратно между выходом :-). Я оставляю это как справочное время.

1 голос
/ 12 июня 2016

Существует очень простой и быстрый data.table подход к этому:

library(data.table)

setDT(dt)[, .(avg = mean(TradeResult.Currency.[which(TradeResult.Currency.>0 )])), by= Instrument]

#    Instrument avg
# 1:        JPM 225
# 2:        KFT 116

Benchmark: Используя сравнение производительности @Joris и @ Chase, это решение почти в пять раз быстрее, чем подход ddply, и в 40 раз быстрее, чем подход aggregate.

tmpData <- data.frame(
        instrument = rep(c("JPM","KFT"),each=10e6),
        TCurr = runif(20e6,-10,100))

system.time( ddply(tmpData, .(instrument), summarise, avgProfit = mean(TCurr[TCurr > 0]))  )
# user  system elapsed 
# 1.41    0.62    2.03 

system.time( setDT(tmpData)[, .(avg = mean(TCurr[which(TCurr>0 )])), by= instrument]  )
# user  system elapsed 
# 0.36    0.18    0.43

system.time( aggregate(TCurr~instrument, mean, data=subset(tmpData,TCurr>0)) )
#  user  system elapsed 
# 16.07    1.81   17.20 
0 голосов
/ 06 декабря 2010

Я бы, наверное, просто подошел к этому из итеративного стиля.У вас должна быть локальная переменная под названием «аккумулятор» или что-то в этом роде, цикл по всем элементам в списке, а также блок if вроде

if (x[index] > 0)
    accumulator = accumulator + x[index]

и возвращение значения аккумулятора, когда вы закончите.

...