我需要实现FIFO算法

I need to realize FIFO algorithm

我有一个像这样的数据框:

items = data.frame(
  Items = c('Item A', 'Item B','Item A','Item A','Item A','Item B','Item B','Item A','Item C'),
  Quantity = c(5,100,4,7,10,50,30,1,1000),
  BuySell = c('B','B','B','S','B','S','S','S','B'),
  Price = c(100,50,110,130,90,45,60,120,5)
)

items$Value = items$Quantity * items$Price

我需要按名称对项目进行分组,并使用 FIFO 方法计算余额的数量和价值。当我们销售一件商品时,我们首先按日期销售(table 按操作日期分组)。我们需要计算未售出商品的价值。

我需要通过 FIFO(先进先出)获得运动项目(买卖)的结果及其价值

在我的示例中,结果必须是:

Item A:
 Quantity: 11 Value 1000 
Item B:
 Quantity: 20 Value 1000
Item C:
 Quantity: 1000 Value 5000

首先,将数量列转换为包含 positive/negative for buy/sell:

items$Quantity <- items$Quantity * ifelse(items$BuySell=="B",1,-1)

接下来,探索如何使用数据的子集执行逻辑,例如对于项目 B:

x <- items[items$Items == "Item B",]
unsold <- sum(x$Quantity)
x <- x[seq(nrow(x),1,-1),]  # reverse order (unsold items are at bottom)
x <- x[x$Quantity > 0,]     # consider buy only
x$cs <- cumsum(x$Quantity)  # cumulative bought amount
x$cs <- pmin(x$cs, unsold)
if(nrow(x) > 1) x[-1,"cs"] <- diff(x$cs) # cs column now holds the amount relevant for cost calculation

这会给你:

 list("Quantity" = unsold, "Value" = sum(x$cs * x$Price))
#$Quantity
#[1] 20
#
#$Value
#[1] 1000

接下来我们把then包装成一个函数,这样我们就可以通过数据框的一个子集来调用它:

calculate.lv <- function(x){

    unsold <- sum(x$Quantity)
    x <- x[seq(nrow(x),1,-1),]  # reverse order (unsold items are at bottom)
    x <- x[x$Quantity > 0,]     # consider buy only
    x$cs <- cumsum(x$Quantity)  # cummulative bought amount
    x$cs <- pmin(x$cs, unsold)
    if(nrow(x) > 1) x[-1,"cs"] <- diff(x$cs)    # cs column now holds the amount relevant for cost calculation
    
    list("Quantity" = unsold, "Value" = sum(x$cs * x$Price))
}

calculate.lv(items[items$Items=="Item C",])
#$Quantity
#[1] 1000
#
#$Value
#[1] 5000

最后,我们使用by函数将函数应用于数据帧的切片:

by(items, list(items$Items), calculate.lv)
#: Item A
#$Quantity
#[1] 11
#
#$Value
#[1] 1010
#
#------------------------------------------------------------ 
#: Item B
#$Quantity
#[1] 20
#
#$Value
#[1] 1000
#
#------------------------------------------------------------ 
#: Item C
#$Quantity
#[1] 1000
#
#$Value
#[1] 5000
#

另一个使用 dplyrtidyr 的选项是

library(dplyr)
library(tidyr)

items <- items %>% 
  group_by(Items) %>% 
  mutate(index = 1:n()) %>% 
  spread(BuySell, Quantity, fill = 0) %>% 
  arrange(Items, index) %>% 
  mutate(TotalStock = cumsum(B) - cumsum(S), 
         Sold = case_when(B == 0 ~ 0, # Nothing bought - cannot be sold
                          cumsum(B) < sum(S) ~ B, # Total items bought is smaller than total item sold - everything is sold
                          sum(S) < (cumsum(B) - B) ~ 0, # Total sum is smaller than total amount bought excluding the current buy - nothing sold
                          TRUE ~ B - (cumsum(B) - sum(S))), 
         InStock = B - Sold) 

这给出了以下 data.frame

items
# A tibble: 9 x 9
# Groups:   Items [3]
#   Items  Price Value index     B     S TotalStock  Sold InStock
#   <fct>  <dbl> <dbl> <int> <dbl> <dbl>      <dbl> <dbl>   <dbl>
# 1 Item A   100   500     1     5     0          5     5       0
# 2 Item A   110   440     2     4     0          9     3       1
# 3 Item A   130   910     3     0     7          2     0       0
# 4 Item A    90   900     4    10     0         12     0      10
# 5 Item A   120   120     5     0     1         11     0       0
# 6 Item B    50  5000     1   100     0        100    80      20
# 7 Item B    45  2250     2     0    50         50     0       0
# 8 Item B    60  1800     3     0    30         20     0       0
# 9 Item C     5  5000     1  1000     0       1000     0    1000

这可以概括为

items %>% 
  summarize(Value = sum(InStock * Price), 
            TotalStock = sum(InStock))

# A tibble: 3 x 3
#   Items  Value TotalStock
#   <fct>  <dbl>      <dbl>
# 1 Item A  1010         11
# 2 Item B  1000         20
# 3 Item C  5000       1000