I modified my previous answer a little bit. The matrix "RentPerDay" is not necessary. "colSums(t(countDays)*RentPerDay)" can be replaced by a matrix-vector-product. This solution calculates the same rental income as the previous solution.
library(lubridate)
ultimo_day <- function( start, end )
{
N <- 12*(year(end) - year(start)) + month(end) - month(start) + 1
d <- start
day(d) <- 1
month(d) <- month(d) + (1:N)
return( d - as.difftime(1,units="days"))
}
countDays <- function( data, d )
{
return( pmin( pmax( outer( d, data$"StartDate", "-") + 1, 0 ), day(d) ) -
pmin( pmax( outer( d, data$"EndDate" , "-"), 0 ), day(d) ) )
}
rentalIncome <- function( data,
d = ultimo_day( min(data$StartDate), max(data$EndDate) ) )
{
return ( data.frame( date = d,
income = ( countDays(data,d) / days_in_month(d) ) %*% data$"MonthlyRental" ) )
}
# -------- Example Data: --------
df1 <- data.frame(
StartDate = as.Date(c("2015-07-01", "2015-06-01", "2015-07-15", "2015-08-01", "2014-06-20")),
EndDate = as.Date(c("2015-09-30", "2015-10-31", "2016-01-31", "2015-12-31", "2015-07-31")),
MonthlyRental = c(500, 600, 400, 800, 300)
)
To the example I added one more lease, which is active for more than one year:
> df1
StartDate EndDate MonthlyRental
1 2015-07-01 2015-09-30 500
2 2015-06-01 2015-10-31 600
3 2015-07-15 2016-01-31 400
4 2015-08-01 2015-12-31 800
5 2014-06-20 2015-07-31 300
"ultimo_day(start,end)" is the vector of days between "start" and "end" on which rent is payed:
> d <- ultimo_day( min(df1$StartDate), max(df1$EndDate))
> d
[1] "2014-06-30" "2014-07-31" "2014-08-31" "2014-09-30" "2014-10-31" "2014-11-30" "2014-12-31" "2015-01-31" "2015-02-28" "2015-03-31" "2015-04-30"
[12] "2015-05-31" "2015-06-30" "2015-07-31" "2015-08-31" "2015-09-30" "2015-10-31" "2015-11-30" "2015-12-31" "2016-01-31"
The rows of the matrix "countDays" correspond to these ultimo days and therefore to the months:
> countDays(df1,d)
Time differences in days
[,1] [,2] [,3] [,4] [,5]
[1,] 0 0 0 0 11
[2,] 0 0 0 0 31
[3,] 0 0 0 0 31
[4,] 0 0 0 0 30
[5,] 0 0 0 0 31
[6,] 0 0 0 0 30
[7,] 0 0 0 0 31
[8,] 0 0 0 0 31
[9,] 0 0 0 0 28
[10,] 0 0 0 0 31
[11,] 0 0 0 0 30
[12,] 0 0 0 0 31
[13,] 0 30 0 0 30
[14,] 31 31 17 0 31
[15,] 31 31 31 31 0
[16,] 30 30 30 30 0
[17,] 0 31 31 31 0
[18,] 0 0 30 30 0
[19,] 0 0 31 31 0
[20,] 0 0 31 0 0
Row 1 belongs to June 2014, Row 2 to July 2014,..., Row 20 to January 2016.
"countDays(df1,d) / days_in_month(d)" is again a matrix.
The (i,j)-component of this matrix is not the number of days
the j-th lease is active in the i-th month, but the fraction of this number by the
length of the i-th month:
> countDays(df1,d) / days_in_month(d)
Time differences in days
[,1] [,2] [,3] [,4] [,5]
[1,] 0 0 0.0000000 0 0.3666667
[2,] 0 0 0.0000000 0 1.0000000
[3,] 0 0 0.0000000 0 1.0000000
[4,] 0 0 0.0000000 0 1.0000000
[5,] 0 0 0.0000000 0 1.0000000
[6,] 0 0 0.0000000 0 1.0000000
[7,] 0 0 0.0000000 0 1.0000000
[8,] 0 0 0.0000000 0 1.0000000
[9,] 0 0 0.0000000 0 1.0000000
[10,] 0 0 0.0000000 0 1.0000000
[11,] 0 0 0.0000000 0 1.0000000
[12,] 0 0 0.0000000 0 1.0000000
[13,] 0 1 0.0000000 0 1.0000000
[14,] 1 1 0.5483871 0 1.0000000
[15,] 1 1 1.0000000 1 0.0000000
[16,] 1 1 1.0000000 1 0.0000000
[17,] 0 1 1.0000000 1 0.0000000
[18,] 0 0 1.0000000 1 0.0000000
[19,] 0 0 1.0000000 1 0.0000000
[20,] 0 0 1.0000000 0 0.0000000
This matrix is multiplied by the vector "df1$MonthlyRental" and the resulting vector is stored as "income" in the data.frame of rental income:
> rentalIncome(df1)
date income
1 2014-06-30 110.000
2 2014-07-31 300.000
3 2014-08-31 300.000
4 2014-09-30 300.000
5 2014-10-31 300.000
6 2014-11-30 300.000
7 2014-12-31 300.000
8 2015-01-31 300.000
9 2015-02-28 300.000
10 2015-03-31 300.000
11 2015-04-30 300.000
12 2015-05-31 300.000
13 2015-06-30 900.000
14 2015-07-31 1619.355
15 2015-08-31 2300.000
16 2015-09-30 2300.000
17 2015-10-31 1800.000
18 2015-11-30 1200.000
19 2015-12-31 1200.000
20 2016-01-31 400.000