<jupyter_start><jupyter_text>Electric Vehicle Price and Sales Tax ExemptionThis notebook is an independent t-test analysis using electric vehicles (EVs) in the state of Washington from 1998 to 2020.By <NAME>Data Source: Electric Vehicle Data from Department of Revenue Washington State. The data contains information for 62261 electric vehicles for model years 1993 to 2021 in Washington state.According to the Washington Departmetn of Revenue, vehicles that can travel at least 30 miles on battery power alone qualify for a sales tax exemption up to 25,000 dollars of the price of a vehicle.URL: https://data.wa.gov/Transportation/Electric-Vehicle-Population-Data/f6w7-q2d2We're interested in the price of electric vehicles and how that impacts sales tax exemptions. The data contains information on over 62,000 EVs sold in Washington between 1998 to 2020. Because some (base manufactured retail price (MSRP) are missing, this analysis looks only at the years 2008 through 2020. We're interested in whether or not a car is eligible for the sales tax exemption and what the price of the EV is. Specifically, we're asking whether there is any difference in the mean price of cars that qualify and those that don't. This information may be useful to automakers and legislators alike. Automakers may adjust sales and marketed efforts if it turns out that the price of a car does not impact whether it qualifies for a tax exemption. Lawmakers may adjust tax policy on electric vehicles to either increase tax revenue or increase environmental sustainability efforts.Our null hypothesis is that there is no difference in the median price between EVs that qualify and those that don't.Our alternative hypothesis is that there is a significant difference between EVs that qualify and those that don't. The data reveals that both price (MSRP) and eligibility (yes or no) are both not normally distributed based on Q-Q plots. Instead of using a t-test on normally distributed data, we turn to the Mann-Whitney U test which is the nonparametric alternative t-test.<jupyter_code>import pandas as pd
import numpy as np

# pip install plotly
import plotly.graph_objects as go
import plotly.express as px

import seaborn as sns
import matplotlib.pyplot as plt
import statsmodels.api as sm

from scipy import stats

%matplotlib inline
df = pd.read_csv('Electric_Vehicle_Population_Data.csv')
df.head()
# looking at number of columns and rows and data types for Electric Vehicle Type, CAFV and Base MSRP.

df.info()
# changing columns names to remove spaces

df.columns = df.columns.str.replace(' ','_')
df.columns
# EV models that do not have a base price by model year
# there are 8542 vehicles that have a 0 for price in the Base_MSRP column

df.query('Base_MSRP==0')['Model_Year'].value_counts()
# which models don't have a price listed

df.query('Base_MSRP==0')['Model'].value_counts()
# removing 8542 cars without a price. 7926 of them are 2020 model years
# consider whether removing this many late model vehicles may bias a ttest. 

indexNames = df[ df['Base_MSRP'] == 0 ].index
df.drop(indexNames , inplace=True)

df.info()
df['Model_Year'].value_counts()
# removing 1 Porsche 918 Spyder as an outlier at $845,000

spyder = df[ df['Base_MSRP'] == 845000 ].index
df.drop(spyder , inplace=True)

df.describe()
# shortening column name based on EV's that are eligible for Washington state tax exemptions
# eligible = 1 and not eligible = 0

df['Eligibility'] = df['Clean_Alternative_Fuel_Vehicle_(CAFV)_Eligibility'].replace({'Not eligible due to low battery range': 0,'Clean Alternative Fuel Vehicle Eligible': 1})
df
df.describe()
# how many of each vehicle type
df['Electric_Vehicle_Type'].value_counts()
# how many EVs are eligible for the tax break and how many are not
# both samples sizes are large at 51,494 and 10,767 cars of each type
df['Eligibility'].value_counts()
# replacing values for Eligibility
# eligible = 1
# not eligible = 0

eligible_yes = df[(df['Eligibility'] == 1)]
eligible_yes.info()

eligible_no = df[(df['Eligibility'] == 0)]
eligible_no.info()
# price is not normally distributed
sns.distplot(df['Base_MSRP'])
plt.show()
# price is not normally distributed
# skew may be within normal limits but kurtosis is greater than 3

plt.hist(df['Base_MSRP'], bins=40, label = 'Price')
plt.legend(loc="upper right")
print(stats.describe(df['Base_MSRP']))
plt.show()
# creating histgrams of eligible and not eligible EVs to look at distribution
# eligible EVs is not normally distributed but skew and kurtosis are within normal limits to do a ttest

plt.hist(eligible_yes['Base_MSRP'], bins=40, label = 'Eligible')
plt.legend(loc="upper right")
print(stats.describe(eligible_yes['Base_MSRP']))
plt.show()
# EVs not eligible are not normally distributed
# both skew and kurtosis are high and greater than 3 so a ttest may not reveal dependable results

plt.hist(eligible_no['Base_MSRP'], bins=40, label = 'Not eligible')
plt.legend(loc="upper right")
print(stats.describe(eligible_no['Base_MSRP']))
plt.show()
# boxplot reveals a lot of outliers in price especially for EVs not eligible
# is it a good idea to remove EV costing over $120k?
# log transform using y=np.log(df['Base_MSRP']) did not improve normality

sns.boxplot(x='Eligibility', y='Base_MSRP', data=df)
plt.show()
# violin plot reveals a midrange gap in price for both EVs that qualify for tax exemptions and those that don't
# EVs that are eligible for tax exemptions have a bigger gap in price. 
# perhaps we might look at whether standard EVs or luxury EVs qualify for tax exemptions

sns.violinplot(x='Eligibility', y='Base_MSRP', data=df)
plt.show()<jupyter_output>C:\Users\Mike\Anaconda3\lib\site-packages\scipy\stats\stats.py:1713: FutureWarning: Using a non-tuple sequence for multidimensional indexing is deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`. In the future this will be interpreted as an array index, `arr[np.array(seq)]`, which will result either in an error or a different result.
  return np.add.reduce(sorted[indexer] * weights, axis=axis) / sumval<jupyter_text>Means are not great to compare on using visuals<jupyter_code># checking eligiblity normality with QQ plots
# normality assumption is not valid

sm.qqplot(eligible_yes['Base_MSRP'], line='s')
plt.title('Eligible EVs')
plt.show()
# checking not eligiblity normality with QQ plots
# normality assumption is not valid

sm.qqplot(eligible_no['Base_MSRP'], line='s')
plt.title('Not Eligible EVs')
plt.show()
t, p = stats.ttest_ind(eligible_yes['Base_MSRP'], eligible_no['Base_MSRP'])
t, p
p< 0.05<jupyter_output><empty_output><jupyter_text>We need a nonparametric t-test for two independent samples. Wilcoxon Rank Sum Test or Kruskal-Wallis or the Mann-Whitney?M-W is used when measurements are independent, samples have equal variances and > 20 per sample, OR normality of the population of every sample is verified OR assumed. Tests if price is different based on rank instead of mean.Neither the independent variables or price are normally distributed and are positively skewed. A ttest ran on this data revealed a significant difference but results are not dependable. We'll run a Mann-Whitney test on the data because we only have two independent variables with similar but nonparametric distributions. We'll look for differences by medians rather than means.* $H_0$: Median price between eligible and not eligible vehicles are the same.* $H_a$: Median price between eligible and not eligible vehicles are significantly different.<jupyter_code># creating a new list of booleans of vehicles by eligibility
# required for Mann-Whitneytest using PyNonpar package
# scipy package for Mann-Whitney does not return a z value which we may need if sig

price_eligible = df['Eligibility'] == 1

price_noneligible = df['Eligibility'] == 0
price_noneligible.head()
# creating a new list of price for Mann-Whitney test using PyNonpar package

price = df['Base_MSRP']
price.value_counts()
# grouping Base_MSRP and Eligiblity for Mann-Whitney test

sample1 = price[price_eligible]
sample2 = price[price_noneligible]
sample1.describe().median()
sample2.describe().median()
U, p =stats.mannwhitneyu(sample1, sample2)
U,p
# results (160930993.0, 0.0)<jupyter_output><empty_output><jupyter_text>Using a Mann-Whitney U test reveals a p-value = or less than 0.  We can reject the null hypothesis and accept the alternative hypothesis that there is a significan difference in the median price between vehicles that are eligible for the sales tax exemption in Washington state and vehicles that are not.  To determine how different they are, we will get a confidence interal using a bootstrap method to report a more meaningful range of difference.<jupyter_code># code to bootstrap a CI from my two samples

med_diffs = []

for i in range(10000):
    eligible = sample1.sample(frac=1.0, replace=True)
    noneligible = sample2.sample(frac=1.0, replace=True)

    med_diff = eligible.median() - noneligible.median()
    med_diffs.append(med_diff)
ci_lo = np.percentile(med_diffs, 2.5)
ci_hi = np.percentile(med_diffs, 97.5)
# eligible is bigger median 2,600 to 2,880

(ci_lo, ci_hi)<jupyter_output><empty_output><jupyter_text>Because we subtracted noneligible vehicle median price from eligible vehicle price, positive numbers in our CI results indicate that eligible vehicles have a higher median price than noneligible vehicles. We can report with 95% confidence that vehicles that do qualify the Washington state sales tax exemptions have a higher median MSRP anywhere from 2,600 to 2,880 dollars.<jupyter_code>plt.hist(med_diffs)
plt.axvline(ci_lo, c="orange")
plt.axvline(ci_hi, c="orange")
plt.show()<jupyter_output><empty_output>