R gurus,
I would like to write a function to apply dynamic prices based on quantities purchased.
Here is the dataset.
prices <- data.frame(from = c(0,101,201,301,401,501,601,701,801,901,1001,1101,2001),
to = c(100,200,300,400,500,600,700,800,900,1000,1100,2000,10000),
price = c(50,45,40,35,30,25,20,15,10,8,7,6,5))
purchases <- data.frame(customer = LETTERS[1:20],
quantity = c(305,100,70,576,687,1200,5007,491,870,320,80,295,680,1100,1305,1024,1800,7400,3500,730),
bill = NA)
purchases dataset has quantities and price dataset has sliding scale prices for different quantity ranges.
For example, customer A purchased 305 units. To calculate billing for this quantity, first hundred units will be billed at $50, second hundred units at $45, third hundred units at $40 and remaining 5 units at $35. Mathematically:
purchases$bill[1] = 100*50 + 100*45 + 100*40 + 5*35
OR
purchases$bill[1] = 100*prices$price[1] + 100*prices$price[2] + 100*prices$price[3] + 5*prices$price[4]
I wonder what is the best way to do this using an R function to calculate bill for each purchase.
Any help is much appreciated.
Using base R we dan do something like below:
c(prices$price%*%diff(replace(A<-outer(c(0,prices$to),purchases$quantity,"-"),A>0,0)))
[1] 13675 5000 3500 21900 24240 29100 48935 19730 26700 14200 4000 13300 24100 28500 29730 27968
[17] 32700 60900 41400 24950
Elaboration:
price=prices$price
lowr=c(0,prices$to)
qnty=purchases$quantity
x=outer(lowr,qnty,"-")
M=diff(replace(x,x>0,0))
colSums(price*M)##similar to c(price%*%M)
transform(purchases,bill=colSums(price*M))
transform(purchases,bill=colSums(price*M))
customer quantity bill
1 A 305 13675
2 B 100 5000
3 C 70 3500
4 D 576 21900
5 E 687 24240
6 F 1200 29100
7 G 5007 48935
8 H 491 19730
9 I 870 26700
10 J 320 14200
11 K 80 4000
12 L 295 13300
13 M 680 24100
14 N 1100 28500
15 O 1305 29730
16 P 1024 27968
17 Q 1800 32700
18 R 7400 60900
19 S 3500 41400
20 T 730 24950
Here is an example of a bad solution. It is not 100% accurate either.
library(dplyr)
prices <- data.frame(from = c(0,101,201,301,401,501,601,701,801,901,1001,1101,2001),
to = c(100,200,300,400,500,600,700,800,900,1000,1100,2000,10000),
price = c(50,45,40,35,30,25,20,15,10,8,7,6,5))
purchases <- data.frame(customer = LETTERS[1:20],
quantity = c(305,100,70,576,687,1200,5007,491,870,320,80,295,680,1100,1305,1024,1800,7400,3500,800),
bill = NA)
prices$qty = prices$to - prices$from + 1
prices$qty[1] = prices$to[1]
prices$c_qty = cumsum(prices$qty)
prices$bill = prices$qty * prices$price
prices$c_bill = cumsum(prices$bill)
prices$id = 1:nrow(prices)
calculate_billing <- function(qty) {
if(qty <= 100){ price_case = 1}
if(qty >= 101 & qty <= 200) { price_case = 2}
if(qty >= 201 & qty <= 300) { price_case = 3}
if(qty >= 301 & qty <= 400) { price_case = 4}
if(qty >= 401 & qty <= 500) { price_case = 5}
if(qty >= 501 & qty <= 600) { price_case = 6}
if(qty >= 601 & qty <= 700) { price_case = 7}
if(qty >= 701 & qty <= 800) { price_case = 8}
if(qty >= 801 & qty <= 900) { price_case = 9}
if(qty >= 901 & qty <= 1000) { price_case = 10}
if(qty >= 1001 & qty <= 1100) { price_case = 11}
if(qty >= 1101 & qty <= 2000) { price_case = 12}
if(qty >= 2001 & qty <= 10000){ price_case = 13}
if(price_case==1) {
billing = prices$price[price_case]*qty
}
if(price_case>1 & price_case<=11 ) {
remainder <- qty%%100
billing = prices$c_bill[price_case-1] + prices$price[price_case]*remainder
}
if(price_case==12) {
remainder <- qty - 1100
billing = prices$c_bill[price_case-1] + prices$price[price_case]*remainder
}
if(price_case==13) {
remainder <- qty - 2000
billing = prices$c_bill[price_case-1] + prices$price[price_case]*remainder
}
return(billing)
}
purchases %>%
rowwise() %>%
mutate(bill = calculate_billing(quantity))
Related
I want to recreate in R the following table :
I have been provided with only these three parameters:
initial_deposit = 15000
contract_value = 250
closing_stock_indices = c(1000, 1002, 994, 998, 997)
My effort in R is this:
cl =c(1000,1002,994,998,997) # The closing stock indices.
re = c(0,diff(cl))
t = time(cl)
mtm = re*250 # The contract value of 250.
mb = 15000+mtm # The initial deposit of 15000.
vm = ifelse(mb>0,0,mtm)
d = data.frame(t,cl,re,mtm,mb,vm);d
but I cannot do the last two columns.Any help ?
You may do the following
library(tidyverse)
initial_deposit = 15000
contract_value = 250
closing_stock_indices = c(1000, 1002, 994, 998, 997)
(seq_along(closing_stock_indices) - 1) %>%
as.data.frame() %>%
setNames('Day') %>%
mutate(Closing_SI = closing_stock_indices,
Daily_change = c(0, diff(Closing_SI)),
Marking_to_market = contract_value * Daily_change,
Margin_balance = accumulate(Marking_to_market[-1], .init = initial_deposit,
~ if (.x >= initial_deposit) .x + .y else initial_deposit + .y),
Variation_Margin = -1 * pmin(Margin_balance - initial_deposit, 0),
REquired_Deposit = c(initial_deposit, Variation_Margin[-n()]))
Day Closing_SI Daily_change Marking_to_market Margin_balance Variation_Margin REquired_Deposit
1 0 1000 0 0 15000 0 15000
2 1 1002 2 500 15500 0 0
3 2 994 -8 -2000 13500 1500 0
4 3 998 4 1000 16000 0 1500
5 4 997 -1 -250 15750 0 0
Check on another vector
initial_deposit = 15000
contract_value = 250
closing_stock_indices = c(1000, 1002, 994, 998, 997, 990, 1000)
(seq_along(closing_stock_indices) - 1) %>%
as.data.frame() %>%
setNames('Day') %>%
mutate(Closing_SI = closing_stock_indices,
Daily_change = c(0, diff(Closing_SI)),
Marking_to_market = contract_value * Daily_change,
Margin_balance = accumulate(Marking_to_market[-1], .init = initial_deposit,
~ if (.x >= initial_deposit) .x + .y else initial_deposit + .y),
Variation_Margin = -1 * pmin(Margin_balance - initial_deposit, 0),
REquired_Deposit = c(initial_deposit, Variation_Margin[-n()]))
Day Closing_SI Daily_change Marking_to_market Margin_balance Variation_Margin REquired_Deposit
1 0 1000 0 0 15000 0 15000
2 1 1002 2 500 15500 0 0
3 2 994 -8 -2000 13500 1500 0
4 3 998 4 1000 16000 0 1500
5 4 997 -1 -250 15750 0 0
6 5 990 -7 -1750 14000 1000 0
7 6 1000 10 2500 17500 0 1000
I'm a relative beginner with R so apologies for the simplistic question.
I have a simple data frame with columns x, y and z. They all contain numerical values and I'd like to write a piece of code that allows me to replaces a all z values with "115" whenever 300 < x < 600, 0 < y < 100, and z > 160.
Very simple problem but I am not sure why I am having so much trouble figuring out how to piece together code for this. I'm sure its some hodge-podge of replace and ifelse arguments but I can't seem to put it together.
Help is much appreciated! Thanks!
This is how I would do it:
library(tidyverse)
set.seed(1)
df <- data_frame("x" = sample(x = 200:700, size = 10, replace = TRUE),
"y" = sample(x = 0:400, size = 10, replace = TRUE),
"z" = sample(x = 0:200, size = 10, replace = TRUE))
df
#> A tibble: 10 x 3
#> x y z
#> <int> <int> <int>
#> 1 523 84 109
#> 2 366 276 164
#> 3 328 361 33
#> 4 617 329 105
#> 5 670 262 125
#> 6 498 328 88
#> 7 469 78 171
#> 8 665 212 32
#> 9 386 36 83
#>10 506 104 162
df$z <- ifelse((df$x > 300 & df$x < 600) & (df$y > 0 & df$y < 100) & (df$z > 160), 115, df$z)
df
#> A tibble: 10 x 3
#> x y z
#> <int> <int> <dbl>
#> 1 523 84 109
#> 2 366 276 164
#> 3 328 361 33
#> 4 617 329 105
#> 5 670 262 125
#> 6 498 328 88
#> 7 469 78 115
#> 8 665 212 32
#> 9 386 36 83
#>10 506 104 162
#(#7 was updated to 115 as it met all the criteria)
Edit
As usual, #TIC's answer is better than mine (fewer steps -> faster) but not by much on my system with a million rows. The data.table method is quickest:
library(tidyverse)
set.seed(1)
df <- data_frame("x" = sample(x = 0:700, size = 1000000, replace = TRUE),
"y" = sample(x = 0:400, size = 1000000, replace = TRUE),
"z" = sample(x = 0:200, size = 1000000, replace = TRUE))
ifelse_func <- function(df){
df$z <- ifelse((df$x > 300 & df$x < 600) & (df$y > 0 & df$y < 100) & (df$z > 160), 115, df$z)
}
transform_func <- function(df){
transform(df, z = replace(z, 300 < x & x < 600 & 0 < y & y < 100 & z > 160, 115))
}
rowsums_func <- function(df){
df$z[!rowSums(!(df >list(300, 0, 160) & df < list(600, 100, Inf)))] <- 115
}
library(data.table)
dt_func <- function(df){
setDT(df)
df[x > 300 & x < 600 & y > 0 & y < 100 & z > 160, z := 115]
}
mbm <- microbenchmark::microbenchmark(ifelse_func(df), transform_func(df),
rowsums_func(df), dt_func(df))
autoplot(mbm)
Edit 2
> system.time(ifelse_func(df))
user system elapsed
0.064 0.020 0.085
> system.time(transform_func(df))
user system elapsed
0.060 0.009 0.069
> system.time(rowsums_func(df))
user system elapsed
0.090 0.021 0.110
> system.time(dt_func(df))
user system elapsed
0.036 0.003 0.039
Do you want this?
transform(
df,
z = replace(z, 300 < x & x < 600 & 0 < y & y < 100 & z > 160, 115)
)
Another option in base R is with rowSums
df$z[!rowSums(!(df >list(300, 0, 160) & df < list(600, 100, Inf)))] <- 115
So we can do this with an ifelse conditions:
Some sample data:
df <- data.frame(x=c(450, runif(10)*200),
y=c(50, runif(10)*100),
z=c(170, runif(10)*100))
> df
x y z
1 450.00000 50.00000 170.00000
2 10.38674 93.33277 74.72619
3 117.66350 48.88015 27.60769
4 128.85086 35.74645 61.32745
5 93.21923 87.15894 53.37949
6 30.09869 86.72846 94.64611
7 104.03966 55.12932 89.78309
8 17.48741 16.50095 42.26284
9 183.52845 39.65171 27.60766
10 79.68355 18.14510 84.17454
11 110.14051 77.85835 33.67199
Then run this:
df$z <- ifelse(df$x > 300 & df$x < 600 & df$y > 0 & df$y < 100 & df$z > 160, 115, df$z)
And we get this:
> df
x y z
1 450.00000 50.00000 115.00000
2 10.38674 93.33277 74.72619
3 117.66350 48.88015 27.60769
4 128.85086 35.74645 61.32745
5 93.21923 87.15894 53.37949
6 30.09869 86.72846 94.64611
7 104.03966 55.12932 89.78309
8 17.48741 16.50095 42.26284
9 183.52845 39.65171 27.60766
10 79.68355 18.14510 84.17454
11 110.14051 77.85835 33.67199
I am trying to create a new wage group variable based on a continuous wage variable I already have. This is the code I have and it has worked well on other similar cases:
df <- df %>% mutate(wagegroup = case_when(wage_total < 100 ~ 'below 100 €/m',
wage_total >= 100 & wage_total <= 546 ~ '100 - 546 €/m',
wage_total >= 546,1 & wage_total <= 1000 ~ '565,1 - 1000 €/m',
wage_total >= 1000,1 & wage_total <= 1500 ~ '1000,1 - 1500 €/m',
wage_total >= 1500,1 & wage_total <= 2000 ~ '1500,1 - 2000 €/m',
wage_total >= 2000,1 ~ 'over 2000,1 €/m'))
I get an error code :
x Case 3 (`wage_total < 100 ~ "below 100 \200/m"`) must be a two-sided formula, not a logical vector.
I wonder what is wrong here? I am just a beginner with Rstudio, so I would very much appreciate the help :)
You've got what appear to be some extraneous characters in your code. I tried to take my best guess at what you are trying to do:
df <- data.frame(wage_total = c(100, 200, 300, 500, 600, 1020, 1038))
df <- df %>% mutate(wagegroup = case_when(wage_total < 100 ~ 'below 100 €/m',
wage_total >= 100 & wage_total <= 546 ~ '100 - 546 €/m',
wage_total >= 546 & wage_total <= 1000 ~ '565 - 1000 €/m',
wage_total >= 1000 & wage_total <= 1500 ~ '1000 - 1500 €/m',
wage_total >= 1500 & wage_total <= 2000 ~ '1500 - 2000 €/m',
wage_total >= 2000 ~ 'over 2000 €/m'))
df
# wage_total wagegroup
# 1 100 100 - 546 €/m
# 2 200 100 - 546 €/m
# 3 300 100 - 546 €/m
# 4 500 100 - 546 €/m
# 5 600 565 - 1000 €/m
# 6 1020 1000 - 1500 €/m
# 7 1038 1000 - 1500 €/m
For a couple of months now, I am trying to solve an operational research model with glpk on a mac. The thing is that I have searched throughout the internet and also I have tried to find help in universities based in Athens Greece but it seems that noone has a clue regarding the specific program. So, my MODEL is the following:
#PARAMETERS
#
param P;
param D;
param S;
param R;
param B;
param LS;
param CLNGs;
param Tp;
param H;
param Frest;
param MA;
param f;
param Cvr {1..B};
param Cvf {1..B};
param Vv {1..B};
param Qv {1..B};
param Demand {1..R};
param d {1..P, 1..D};
# VARIABLES
#
var z{1..B} >=0, integer;
var x{1..P,1..R,1..B,1..S,1..D} >=0, continuous;
var y{1..P,1..B,1..R,1..S,1..D} >=0, integer;
# OBJECTIVE FUNCTION
#
minimize F {p<>m}: sum {p in 1..P} sum {m in 1..D}
sum {v in 1..B}Cf[v]*d[p,m]*y[p,m,v] + H* sum {v in 1..B} Cr[v]*z[v] +
sum {s in 1..S} sum {r in 1..R} sum {v in 1..B} CLNG[s]*Q[v]*x[s,r,v];
# CONSTRAINTS
#
s.t. constr1 {r in 1..R, r<>p, r<>m }: sum {p in 1..P}
sum {v in 1..B}Q[v]*x[p,r,v] - sum{m in 1..D}
sum{v in 1..B}Q[v]*x[r,m,v] >=Demand[r];
s.t. constr2 {p in 1..P, r in 1..R, v in 1..B}: y[p,r,v] >= x[p,r,v];
s.t. constr3 { r in 1..R, v in 1..B}:
sum {p in 1..P} x[p,r,v] >= sum {m in 1..D} x[r,m,v];
s.t. constr4 { p in 1..P, v in 1..B, p<>m}:
sum {m in 1..D} y[m,p,v] = sum {m in 1..D} y[p,m,v];
s.t. constr5 {v in 1..B, p<>m}: H*z[v] >= 1/V[v] *sum{p in 1..P}
sum{m in 1..D} d[p,m]*y[p,m,v] +
sum{p in 1..P}*(T[p]*sum{m in 1..D}y[p,m,v]);
s.t. constr6 {p in 1..P, r in 1..R, v in 1..B}:
Q[v]*y[p,r,v] <= Frest*y[p,r,v];
s.t. constr7 {v in 1..LS, i<>j}: sum {i in 1..R}
sum {j in 1..R} x[i,j,v] = 0;
s.t. constr8 {s in 1..S}: sum {r in 1..R}
sum {v in 1..V} Q[v]*x[s,r,v] <= MA[s];
s.t. constr9 {s in 1..S, r in 1..R}: x[s,r,v] >= f*y[s,r,v];
end;
And my DATA:
#test comments
param P := 7;
param D := 7;
param S := 3;
param R := 4;
param B := 4;
param LS := 2;
param CLNGs := 200;
param Tp := 24;
param H := 30;
param Frest := 1000000000
param MA := 10000000000
param f :=0.8
param Cvr : 1 :=
1 510000
2 600000
3 700000
4 900000
;
param Cvf : 1 :=
1 22
2 26
3 32
4 42
;
param Vv : 1 :=
1 24
2 26
3 28
4 30
;
param Qv : 1 :=
1 5000
2 7500
3 10000
4 15000
;
param Demand : 1 :=
1 2595.9
2 3781.2
3 1668
4 372.9
;
param d: 1 2 3 4 5 6 7:=
1 0 1663 996 306 291 333 372
2 1663 0 2413 1867 1905 1856 2050
3 996 2413 0 924 938 761 1103
4 306 1867 924 0 107 202 110
5 291 1905 938 107 0 122 184
6 333 1856 761 202 122 0 302
7 372 1050 1103 110 184 302 0
;
end;
When I am trying to run this i get always the following error:
thesis.mod:29: syntax error in variable statement
Context: ..... P , 1 .. R , 1 .. B , 1 .. S , 1 .. D } >= 0 ,continuous
MathProg model processing error
Do you have any ideas on how to solve this?
There is no 'continuous' attribute defined in the var statement of the GMPL language. Variables are by default continuous.
Please, read doc/gmpl.pdf that comes with GLPK.
i found out, that you can do modulo using this :
x % m == (x + x / m) & m
but i cannot understand why its working...
like for 8 % 7 == (8 + 8 / 7) & 7, this is
x = 8 = 0001 0000
x / 7 = 1 = 1000 0000
x + x / 7 = 9 = 1001 0000
9 & 7 = 1001 0000 & 1110 0000 = 1000 0000 = 1
N = 7k + m, m<7
N/7 = k
N + N/7 = 8k + m
(N + N/7) & 7 = (8k + m) & 7
= m & 7
= m
It works for any 2n-1 number, not just 7.