*This is part of a series of short posts about politics that seeks to show how we use data science to learn more about the real world. Follow along here*.

```
# define tax brackets
current_brackets <- data.frame(dollars = c(9525,38700,82500,157500,200000,500000,Inf),
tax_rate = c(10,12,22,24,32,35,37)/100)
high_brackets <- data.frame(dollars = c(500,1000,1500,2000,4000,6000,8000,10000,12000,14000,16000,18000,20000,22000,26000,32000,38000,44000,50000,60000,70000,80000,90000,100000,Inf)*5.496,
tax_rate = c(14,15,16,17,19,22,25,28,32,36,39,42,45,48,50,53,55,58,60,62,64,66,68,69,70)/100)
# define a function for paying your taxes
pay_taxes <- function(income,brackets){
# for each bracket, subtract x dollars from income
taxes_total <- 0
#income <- 5000
income_left <- income
for(i in 1:nrow(brackets)){
# get taxed dollars and tax rate
dollars_taxed <- brackets[i,]$dollars -
ifelse(i==1,0,brackets[i-1,]$dollars)
rate_of_tax <- brackets[i,]$tax_rate
# if you're at the top marginal, reset the Inf to = remaining income
if(dollars_taxed == Inf){dollars_taxed <- income_left}
# if residual income <0, set taxed dollars = remaining income
if(income_left - dollars_taxed < 0){dollars_taxed <- income_left}
# calculate taxes paid
taxes_paid <- dollars_taxed * rate_of_tax
# calculate residual income
income_left <- income_left - dollars_taxed
taxes_total <- taxes_total + taxes_paid
# if residual income <0, recalc taxes paid and return
if(income_left == 0){break}
}
return(data.frame(income,taxes_total,on_paper=rate_of_tax))
}
# actual vs perception ----------------------------------------------------
# calc taxes at different incomes
tax_df <-
pblapply(seq(1,6e05,1000),
function(x){
data.frame(type=c('current','high')) %>%
cbind(
rbind(pay_taxes(x,current_brackets))
)
}) %>%
do.call('rbind',.)
tax_df <- tax_df %>%
mutate(tax_rate = taxes_total/income)
# marginal vs perception
m_v_p.gg <- ggplot(tax_df,aes(x=income,y=taxes_total)) +
geom_vline(data=data.frame(x=current_brackets$dollars[1:6]),
aes(xintercept=x),col='gray40',linetype=2) +
geom_line() +
geom_line(aes(x=income,y=on_paper*income),col='red') +
scale_x_continuous(breaks=c(current_brackets$dollars[1:6]),
labels=function(x){paste0("$",round(x/1000),"k")}) +
scale_y_continuous(labels=function(x){paste0("$",round(x/1000),"k")}) +
labs(title="Tax rates aren't simply income times rate",
subtitle="Tax rates are marginal, which means you only pay the higher rate on income above each bracket.\nFor most Americans, this decreases their total tax burden.",
x="Income",
y="Taxes paid",
caption='Source: IRS') +
ggplot2::annotate('text',x=300000,y=115000,label='Not how taxes work',angle=30,col='red') +
ggplot2::annotate('text',x=320000,y=80000,label='Actually how taxes work',angle=30,col='black')
# rate
m_v_p.rates.gg <- ggplot(tax_df,aes(x=income,y=tax_rate)) +
geom_vline(data=data.frame(x=current_brackets$dollars[1:6]),
aes(xintercept=x),col='gray40',linetype=2) +
geom_line() +
geom_line(aes(x=income,y=on_paper),col='red') +
scale_x_continuous(breaks=c(current_brackets$dollars[1:6]),
labels=function(x){paste0("$",round(x/1000),"k")}) +
scale_y_continuous(labels=function(x){paste0(round(x*100,1),"%")},
limits=c(0,0.40)) +
labs(title="Tax rates aren't simply income times rate",
subtitle="Tax rates are marginal, which means you only pay the higher rate on income above each bracket.\nFor most Americans, this decreases their total tax burden.",
x="Income",
y="Cumulative tax rate",
caption='Source: IRS') +
ggplot2::annotate('text',x=300000,y=0.33,label='Not how taxes work',col='red') +
ggplot2::annotate('text',x=320000,y=0.25,label='Actually how taxes work',angle=10,col='black')
# 2018 vs 1970 ---------
# calc
tax_df.comp <-
pblapply(seq(1,6e05,1000),
function(x){
data.frame(type=c('2018','1970')) %>%
cbind(
rbind(pay_taxes(x,current_brackets)) %>%
rbind(pay_taxes(x,high_brackets))
)
}) %>%
do.call('rbind',.)
tax_df.comp <- tax_df.comp %>%
mutate(tax_rate = taxes_total/income)
# plot income
increase.gg <- ggplot(tax_df.comp,aes(x=income,y=taxes_total,col=type)) +
geom_vline(data=data.frame(x=current_brackets$dollars[1:6]),
aes(xintercept=x),col='gray40',linetype=2) +
geom_line() +
scale_x_continuous(breaks=c(current_brackets$dollars[1:6]),
labels=function(x){paste0("$",round(x/1000),"k")}) +
scale_y_continuous(labels=function(x){paste0("$",round(x/1000),"k")}) +
labs(title="What would a return to a 1970s tax rate look like?",
subtitle="The wealthy used to pay a much bigger slice of the federal income pie",
x="Income",
y="Taxes paid",
caption='Source: IRS') +
scale_color_manual("Tax code",values=c('1970'='#E74C3C','2018'='#F39C12'))
# plot rate
increase.rates.gg <- ggplot(tax_df.comp,aes(x=income,y=tax_rate,col=type)) +
geom_vline(data=data.frame(x=current_brackets$dollars[1:6]),
aes(xintercept=x),col='gray40',linetype=2) +
geom_line() +
scale_x_continuous(breaks=c(current_brackets$dollars[1:6]),
labels=function(x){paste0("$",round(x/1000),"k")}) +
scale_y_continuous(labels=function(x){paste0(round(x*100,1),"%")}) +
labs(title="What would a return to a 1970s tax rate look like?",
subtitle="The wealthy used to pay a much bigger slice of the federal income pie...",
x="Income",
y="Cumulative tax rate",
caption='Source: IRS') +
scale_color_manual("Tax code",values=c('1970'='#E74C3C','2018'='#F39C12'))
tax_df.comp2 <-
pblapply(seq(1,10e06,10000),
function(x){
data.frame(type=c('2018','1970')) %>%
cbind(
rbind(pay_taxes(x,current_brackets)) %>%
rbind(pay_taxes(x,high_brackets))
)
}) %>%
do.call('rbind',.)
tax_df.comp2 <- tax_df.comp2 %>%
mutate(tax_rate = taxes_total/income)
# plot
big_money.gg <- ggplot(tax_df.comp2,aes(x=income,y=taxes_total/income,col=type)) +
geom_vline(data=data.frame(x=current_brackets$dollars[1:6]),
aes(xintercept=x),col='gray40',linetype=2) +
geom_line() +
scale_x_continuous(labels=function(x){ifelse(x<1e06,paste0("$",round(x/1000),"k"),paste0("$",round(x/1000000),"m"))}) +
scale_y_continuous(labels=function(x){paste0(round(x*100,1),"%")}) +
labs(title="What would a return to a 1970s tax rate look like?",
subtitle="The wealthy used to pay a much bigger slice of the federal income pie...\n...especially the super-wealthy",
x="Income",
y="Cumulative tax rate",
caption='Source: IRS') +
scale_color_manual("Tax code",values=c('1970'='#E74C3C','2018'='#F39C12'))
```

The national news media (especially outlets on the right, including one that rhymes with Schmox News) have made a lot of stink over marginal tax rates recently. It all started with young upstart representative Alexandria Ocasio-Cortez (of New York’s 14th Congressional District) gave an interview to CNN’s Anderson Cooper (for the news show “60 Minutes”) wherein she called for a 70% top marginal tax rate, which she cut bracketed as those making more than $10 million per year. Setting aside the facts that (a) a majority of Americans supports this idea and (b) that very few Americans make that much money, let’s talk about the resulting outrage among conservative media types.

### How marginal taxes actually work

Among the people who misunderstood her call to raise the marginal tax rate as one to raise the total tax rate to 70% is tax law advocate Grover Norquist, who equated a tax that high with slavery. Actually, that’s not how taxes work, as Christopher Ingraham over at the Washington Post so helpfully points out. He made a simple graph that shows how a flat 70% tax rate compares to a marginal tax rate, where different rates are applied to different amounts of income. I recreated his chart here:

`preview(m_v_p.gg)`

Christopher’s figure shows very effectively the difference between being taxed at x% of your TOTAL income and x, y, z, … n% of your MARGINAL income. I like his graph. I propose that we make one ourselves that reflects this not as dollars paid but as the tax rate.

Below, you can see how the marginal system smooths out the jumps in rates between brackets, whereas Norquist’s mischaracterization of the tax rate will increase the taxes that people pay by up to 10% at a time, depending on the bracket:

`preview(m_v_p.rates.gg)`

I like the latter plot because it very clearly shows how the two approaches to taxation are different in rate rates versus dollars; This is how we typically hear about taxes, after all. I reckon that most people don’t know exactly how much they paid the Federal Government in income taxes last year, but they might know which bracket they’re in.

### So what about 1970?

Let’s rewind. What made people go nuts last week was not necessarily the difference between marginal and regular tax rates, but the level of taxation. A 70% rate does *sound* like a high top marginal rate compared to today’s 37%. So let’s look at the difference in both dollars paid and tax rate between the inflation-adjusted 1970s tax brackets and those from 2018:

`preview(increase.gg,themearg=theme(legend.position='top'))`

`preview(increase.rates.gg,themearg=theme(legend.position='top'))`

Of course, a feature of the marginal tax system is that you don’t actually end up paying the rate described on your bracket — unless, that is, you’re very wealthy compared to the average American. If you make $10 million per year, for example, paying 70% on income above $550,000 — the top bracket in inflation-adjusted 1970s dollars — is going to look a lot more like a 70% tax rate than if you’re making just $600,000. The more money you make above the cutoff for your bracket, the closer you come to actually paying that share of your income to the Internal Revenue Service. I show that relationship here:

`preview(big_money.gg,themearg=theme(legend.position='top'))`

So, there you have it. That’s how we can recreate Christopher’s work for WaPo (again, all the credit for this concept goes to him, I just wanted to extend the visualization) and extend it to better understand the differences between the tax rate in 1970 and 2018. Of course, a very obvious disclaimer is warranted: these plots do not take into account the various tax credits and deductions that Americans get for things like having kids or donating to a charity.

#### Tags

#### Related

- R for Political Data Science Week 12: Do Voters Still Care About The Economy?
- R for Political Data Science Week 11: Is Beto O'Rourke the Media's Sweetheart?
- R for Political Data Science Week 10: What If Each State Allocated Their Electoral College Votes Proportionally?