问题 使用R计算边际税率


我正在编写一个函数,根据收入水平计算欠税 澳大利亚的边际税率

我写了一个函数的简单版本,使用以下内容产生正确的税额:

income_tax <- function(income) {
# Calculate income tax liability based on income
#
# Returns the amount of income tax owed

    if (income > 0 & income <= 18200) {
        tax <- 0
        } else if (income > 18200 & income <= 37000) {
        tax <- (income - 18200) * .19
        } else if (income > 37000 & income <= 80000) {
        tax <- 3572 + (income - 37000) * .325
        } else if (income > 80000 & income <= 180000) {
        tax <- 17547 + (income - 80000) * .37
        } else if (income > 180000) {
        tax <- 54547 + (income - 180000) * .45
        }
    return(tax)
}

这种方法的问题在于我将每个括号中的费率和支付金额硬编码到逻辑中。这使得功能变得脆弱,并且意味着我无法测试不同的速率或括号(这是我的最终目标)。

我想要做的是从税率表中生成逻辑。

这是我想用伪代码编写的算法作为注释的版本。

income_tax <- function(income) {
# Calculate income tax liability based on income
#
# Returns the amount of income tax owed
brackets <- c(18200,37001,80000,180000,180000)
rates <- c(0,.19,.325,.37,.45)
tax_rates <- data.frame(brackets, rates)

for (i in 1:nrow(tax_rates)) {
    # if income is in bracket_X then:
    # tax <- (income - bracket_X[i-1]) * rate_X + minimum_tax_from_bracket_X[-1]
    }

return(tax)
}

我的问题是,我无法概念化或编码如何在数据编码时产生欠税额和边际费率。


9833
2017-11-18 02:11


起源



答案:


这是一个可以解决问题的单线程:

income_tax <- 
function(income,
         brackets = c(18200, 37000, 80000, 180000, Inf),
         rates = c(0, .19, .325, .37, .45)) {        
    sum(diff(c(0, pmin(income, brackets))) * rates)
}

也许最简单的方法来看看它是如何工作的/为什么它的工作原理是使用一些更简单的参数来解决核心逻辑,如下所示:

brackets <- c(1:5, Inf)

diff(c(0, pmin(.35, brackets)))
## [1] 0.35 0.00 0.00 0.00 0.00 0.00
diff(c(0, pmin(3.9, brackets)))
## [1] 1.0 1.0 1.0 0.9 0.0 0.0
diff(c(0, pmin(99, brackets)))
## [1]  1  1  1  1  1 94

7
2017-11-18 04:36



我正在给它这个,因为它是最简单的,它也教我关于两个函数pmin和diff。其他答案也有效,但这是最优雅的。 - Tom McMahon


答案:


这是一个可以解决问题的单线程:

income_tax <- 
function(income,
         brackets = c(18200, 37000, 80000, 180000, Inf),
         rates = c(0, .19, .325, .37, .45)) {        
    sum(diff(c(0, pmin(income, brackets))) * rates)
}

也许最简单的方法来看看它是如何工作的/为什么它的工作原理是使用一些更简单的参数来解决核心逻辑,如下所示:

brackets <- c(1:5, Inf)

diff(c(0, pmin(.35, brackets)))
## [1] 0.35 0.00 0.00 0.00 0.00 0.00
diff(c(0, pmin(3.9, brackets)))
## [1] 1.0 1.0 1.0 0.9 0.0 0.0
diff(c(0, pmin(99, brackets)))
## [1]  1  1  1  1  1 94

7
2017-11-18 04:36



我正在给它这个,因为它是最简单的,它也教我关于两个函数pmin和diff。其他答案也有效,但这是最优雅的。 - Tom McMahon


我想你在找 findInterval

income_tax <- function(income, 
                       brackets = c(0, 18200, 37000, 80000, 180000, 180000),
                       rates = c(0, .19, .325, .37, .45)) {
  bracketInd <- findInterval(income, brackets, all.inside = TRUE)
  plus <- if (bracketInd <= 2) 0 else 
    sum(sapply(bracketInd:3, function(ind) {
      (brackets[ind] - brackets[ind - 1]) * rates[ind - 1] 
    }))
  if (length(plus) == 0) plus <- 0
  tax <- plus + (income - brackets[bracketInd]) * rates[bracketInd]
  return(tax)
}

你只需在每个元素之间找到你的 income 是,并将其用作索引 brackets 和 rates。我还将值作为参数添加到默认值。


3
2017-11-18 02:52



你还没有提供一种计算方法 plus 参数何时 brackets 参数变化,在我看来是问题的一个重要方面。 - nathanesau
函数也错误地返回 NA 什么时候 income 超过 tail(brackets,1)。 - nathanesau
@nathanesau 1.这是一个使比例连续的固定值。它只是每年都在变化而且他没有要求在飞行中计算,尽管它确实取决于其他变量。那是固定的。 - Molx
虽然它可能不是他的最终目标,但它解决了使用不同括号和速率的问题。 - Molx
从那以后仍然没有给出正确的输出 plus 是错的。例如,税收 income = 100000 应该是24947 - nathanesau


我稍微改变了你的 brackets 和 rates 参数。您可以使用a来解决此问题 while 循环。

income_tax <- function(income,
    brackets = c(18200,37001,80000,180000),
    rates = c(.19,.325,.37,.45))
{
  nbrackets <- length(brackets)  
  if(income<=brackets[1]) 
    return(0)

  i <- 2
  cumtax <- 0
  while(i <= nbrackets) {
    if(income > brackets[i-1] && income < brackets[i]) 
      return(cumtax + rates[i-1]*(income-brackets[i-1]))
    else 
      cumtax <- cumtax + rates[i-1]*(brackets[i]-brackets[i-1])
      i <- i + 1
  }
  # income > brackets[nbrackets]
  cumtax + rates[nbrackets] * (income - brackets[nbrackets]) 
}

incomes <- seq(0,200000,25000)
round(sapply(incomes,income_tax),0)
# [1]     0  1292  7797 15922 24947 34197 43447 52697 63547

1
2017-11-18 02:46



这有效,但findInterval答案似乎更优雅地解决了问题。 - Tom McMahon