ANOVA

Author

Justin Gould

Published

November 8, 2025

Installed Packages:

To begin, I loaded the tidyverse package and set the working directory to where my data was located. Lastly, I created a dataframe called housing_data to store the data. Next, I performed an exploratory data analysis on the housing data with the goal of being able to fit this data into regression models and predict housing prices using the variable SalePrice.

library(tidyverse)
setwd("C:/Users/justi/OneDrive/Desktop/Grad School/UTSA 1st semester/STA 6443 Statistical Modeling/Midterm") 

housing_data <- read.csv("ames_housing_data-1.csv")  

1. Univariate Analysis

To begin my EDA I created a data frame called eda with the purpose of narrowing down the variables within the housing dataset. I restricted my analysis to only focus on these variables: SalePrice, GrLivArea, LotArea, OverallQual, YearBuilt, GarageCars, Neighborhood, CentralAir. I renamed this revised dataset “eda.”

eda <- housing_data |> select(SalePrice, GrLivArea, LotArea, OverallQual, YearBuilt, GarageCars, Neighborhood, CentralAir)

Data Dictionary

SalePrice: The price the house sold for (dependent variable)

GrLivArea: Above grade (ground) living area square feet

LotArea: Lot size in square feet

OverallQual: Rates the overall material and finish of the house (1-10 scale)

YearBuilt: Original construction date

GarageCars: Size of garage in car capacity

Neighborhood: Physical locations within Ames city limits (categorical)

CentralAir: Central air conditioning (Yes or No)


Next, I ran a histogram plot to observe the distribution of the our dependant variable in this analysis. Sale price will be the dependant variable for this analysis

eda |> ggplot(aes(x=SalePrice)) + 
        geom_histogram(fill = "darkgreen", color = "black") + 
        labs(title = "Sales Price Distribution")

Takeaway: This graph uncovers that this data is right skewed, meaning that the majority of sales price values are in the low to moderate range within this dataset


Next, I evaluated the distribution of the independent variables.

eda |> ggplot(aes(x=OverallQual)) + 
        geom_histogram(fill = "darkgreen", color = "black", bins = 30) + 
        labs(
          title = "Overall Quality Distribution",
          x = "Overall Quality",
          y = "Count"
          )

Takeaway: This graph shows that we will be working with a majority of houses in the 5-7 point quality range.


 eda |> ggplot(aes(x=YearBuilt)) + 
        geom_histogram(fill = "darkgreen", color = "black") + 
        labs(
    title = "Year Built Distribution",
    x = "Year Built",
    y = "Count"
  ) +
  scale_x_continuous(breaks = seq(1870, 2020, by = 20))

Takeaway: This graph shows a pretty even number of houses built in the 50s-70s and then uncovers a massive spike in houses built in the early 2000s.


eda |> 
    filter(LotArea <= 50000) |> 
    ggplot(aes(x=LotArea)) + 
        geom_histogram(fill = "darkgreen", color = "black", bins = 30) + 
        labs(
      title = "Lot Area Distribution", 
      x = "Lot Area",
      y = "Count"
  ) + scale_x_continuous(
    breaks = seq(0, max(eda$LotArea, na.rm = TRUE), by = 10000))

Takeaway: The lot area data is normally distributed in one section and then it right skews. There are many outliers in the 40,000- 100,000 area range but I have applied the filter to normalize the data.


Next, I ran a summary function to evaluate the dependant variable and the categorical variables further.

eda |> summary(SalePrice)
   SalePrice        GrLivArea       LotArea        OverallQual    
 Min.   : 34900   Min.   : 334   Min.   :  1300   Min.   : 1.000  
 1st Qu.:129975   1st Qu.:1130   1st Qu.:  7554   1st Qu.: 5.000  
 Median :163000   Median :1464   Median :  9478   Median : 6.000  
 Mean   :180921   Mean   :1515   Mean   : 10517   Mean   : 6.099  
 3rd Qu.:214000   3rd Qu.:1777   3rd Qu.: 11602   3rd Qu.: 7.000  
 Max.   :755000   Max.   :5642   Max.   :215245   Max.   :10.000  
   YearBuilt      GarageCars    Neighborhood        CentralAir       
 Min.   :1872   Min.   :0.000   Length:1460        Length:1460       
 1st Qu.:1954   1st Qu.:1.000   Class :character   Class :character  
 Median :1973   Median :2.000   Mode  :character   Mode  :character  
 Mean   :1971   Mean   :1.767                                        
 3rd Qu.:2000   3rd Qu.:2.000                                        
 Max.   :2010   Max.   :4.000                                        

Takeaway: The SalePrice mean is 214,000 with the min being 34k and the max being 755k. This wide range shows that this sales price data is going to show us a wide variation of sales prices throughout our analysis.


2. Bivariet Analysis

In phase 2 of my EDA, I investigate the relationships between the dependent variable and the independent variables.

cor(eda$SalePrice, eda$GrLivArea)
[1] 0.7086245
cor(eda$SalePrice, eda$OverallQual)
[1] 0.7909816
cor(eda$SalePrice, eda$LotArea)
[1] 0.2638434
cor(eda$SalePrice, eda$GarageCars)
[1] 0.6404092
cor(eda$SalePrice, eda$YearBuilt)
[1] 0.5228973
cor(eda$OverallQual, eda$GarageCars)
[1] 0.6006707

Takeaway: The strongest correlation is between Sale Price and Overall Quality of .79. I also discovered a medium correlation between two independent variables Overall Quality and GarageCars of .64.


I explore our strongest correlation between Sale Price and Overall quality here…

eda |> ggplot(aes(x=OverallQual, y=SalePrice)) + 
    geom_point(color="darkgreen") +
    geom_smooth(method = "lm", se = FALSE, color = "black") +
    labs(title = "Relationship between Sale Price and Overall Quality")

Takeaway: As quality score increase on the 1-10 scale, sale price increases in a linear fashion with it.


I explore our next strongest corellation here by filtering out the GrLivArea to less than 3,000 sqft to eliminate outliers.

eda |> filter(GrLivArea <= 3000) |> 
    ggplot(aes(x=SalePrice, y=GrLivArea)) + 
    geom_point(color = "darkblue") +
    labs(
            title = "Relationship between Sale Price and Above grade (ground) living area"
          )

Takeaway: This data reveals that Sale Price and Above grade (ground) living area are positively correlated with most of the datapoints moving in linear fasion.


Next, I evaluate the relationship between our dependant variable and one of the categorical variables, cenralair.

eda |> ggplot(aes(x=CentralAir, y=SalePrice)) + 
  geom_boxplot(fill = "lightgreen", color = "black") +
    labs(title = "Relationship between Sale Price and Air Conditioning")

Takeaway: Most of the high value properties contrain central air conditioning units.


Next, I investigate the relationship between Sale Price, GrLivArea, and Overall Quality.

 eda |> ggplot(aes(x = GrLivArea, y = SalePrice, color= OverallQual)) +
    geom_point() +
    labs(title = "Sale price relationship between GrLivArea and Overall Quality")

Takeaway: This graph shows that the larger the above ground living area is, the better the quality, AND the higher the sales price tend to be.


3. Data Cleaning and Transformation

For data cleaning, I checked for outliers within the categorical variables, null values, and references the above charts to take note of any outliers. The outliers I found were in GrLivArea and Lot Area.

eda |> summarize(CentralAir_nulls = sum(is.na(CentralAir)), Neighborhood_nulls = sum(is.na(Neighborhood)))
  CentralAir_nulls Neighborhood_nulls
1                0                  0
colSums(is.na(eda))
   SalePrice    GrLivArea      LotArea  OverallQual    YearBuilt   GarageCars 
           0            0            0            0            0            0 
Neighborhood   CentralAir 
           0            0 

Additionally, I changed row names in the neighborhood column for uniformity.

 unique(eda$Neighborhood)
 [1] "CollgCr" "Veenker" "Crawfor" "NoRidge" "Mitchel" "Somerst" "NWAmes" 
 [8] "OldTown" "BrkSide" "Sawyer"  "NridgHt" "NAmes"   "SawyerW" "IDOTRR" 
[15] "MeadowV" "Edwards" "Timber"  "Gilbert" "StoneBr" "ClearCr" "NPkVill"
[22] "Blmngtn" "BrDale"  "SWISU"   "Blueste"
eda <- eda |> mutate(Neighborhood = str_to_title(Neighborhood))

4. Feature Selection

I believe based off their high correlation with Sale price, that GrLivArea and OverallQual will be the most important variables for predicting sale price in a model. The positive linear relationship that these variables showed in the bivariate analysis prove that these variables can be accurate predictors for sale price.


Part 2.

I added a column to the EDA dataset to conduct a one way ANOVA test called Location. This location column shows the neighborhoods categorized as North, South, or West.

eda2.0 <- eda |> group_by(Neighborhood) |> 
       mutate(Location = case_when(
        Neighborhood %in% c(
                        "Blmngtn", "Brdale", "Brkside", "Gilbert", "Idotrr",
                        "Names", "Noridge", "Npkvill", "Nridght",
                        "Nwames", "Oldtown", "Somerst", "Stonebr") ~ "North",
        Neighborhood %in% c(
                        "Meadowv", "Mitchel", "Timber") ~ "South",
        Neighborhood %in% c(
                        "Blueste", "Clearcr", "Collgcr", "Crawfor", "Edwards",
                        "Swisu", "Sawyer", "Sawyerw", "Veenker") ~ "West"))

AVOVA

Purpose: The purpose of this ANOVA test will be to determine if there is a statistically significant difference in sales between the North, South, and West locations.

Null Hypotheses:

There is no difference in mean Sale Price across all locations (North, South, West).


Alternative Hypotheses:

There is a difference in mean across at least one location.

First off, I used as.factor to change the categorical variables to factors. Next, I gathered the means of saleprice across location.

eda2.0$Neighborhood<-as.factor(eda2.0$Neighborhood)
eda2.0 |> group_by(Location) |> summarize(mean_location = mean(SalePrice))
# A tibble: 3 × 2
  Location mean_location
  <chr>            <dbl>
1 North          185514.
2 South          178254.
3 West           173613.

Next, I created a boxplot to show North, South, and West group means.

eda2.0 |> ggplot(aes(x = Location, y = SalePrice)) +
          geom_boxplot(fill = "lightblue", color = "black") +
          labs(title = "Location Group Means",
               x = "Sale Price",
               y = "Location") +
          theme_minimal()


Next, I conducted the ANOVA test and a Tukeys test to be sure of the results.

oneway <- aov(SalePrice ~ Location , data = eda2.0)
summary(oneway)
              Df    Sum Sq   Mean Sq F value Pr(>F)  
Location       2 4.551e+10 2.275e+10   3.618 0.0271 *
Residuals   1457 9.162e+12 6.289e+09                 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Conclusion: We can conclude from the P > .0271 value that the P value is significant As a result, to reject the null hypothesis and must dig deeper to find which means are different.

To do this, I perform a tukeys test…

TukeyHSD(oneway)
  Tukey multiple comparisons of means
    95% family-wise confidence level

Fit: aov(formula = SalePrice ~ Location, data = eda2.0)

$Location
                  diff       lwr       upr     p adj
South-North  -7260.043 -26579.91 12059.822 0.6520065
West-North  -11901.670 -22373.68 -1429.662 0.0211474
West-South   -4641.627 -24692.76 15409.501 0.8500174

Takeaway: According to the p = .0211474 value, there is significant difference in North and West sales prices. There is no statistical evidence to support the other locations sales prices having any significant difference.


Two Way ANOVA

Null Hypotheses We test two null hypothesis in this test:

  1. There is no difference in mean Sale Price across all locations (North, South, West).
  2. The mean Sale Price is the same across all years houses were built in.

Alternative Hypotheses:

  1. There is a difference in mean across at least one location.
  2. Thrre is a difference in mean sale price accross years build.

To begin my test I grouped the year build column into decade long buckets.

eda2.0$YearBuilt <- paste0(floor(eda2.0$YearBuilt / 10) * 10, "-", floor(eda2.0$YearBuilt / 10) * 10 + 9)

Next, I looked a boxplot graph that shows the means of all of our decades.

eda2.0 |> ggplot(aes(x = YearBuilt, y = SalePrice, fill = YearBuilt)) +
          geom_boxplot(fill = "lightblue", color = "black") +
          labs(title = "Year Built Means",
               x = "Sale Price",
               y = "Year Built") +
          theme_minimal()

Next, I run the two way ANOVA model…

twoway <- aov(SalePrice ~ YearBuilt+Location, data = eda2.0)
summary(twoway)
              Df    Sum Sq   Mean Sq F value   Pr(>F)    
YearBuilt     14 3.232e+12 2.309e+11  56.437  < 2e-16 ***
Location       2 7.198e+10 3.599e+10   8.797 0.000159 ***
Residuals   1443 5.903e+12 4.091e+09                     
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Takeaway: Both of our p values are significant! Meaning we reject both of the null hypothesis. There is statistical evidence to support location and year build impacting sale price.

Next, I run a tukeys test to look deeper…

tukeys <- TukeyHSD(twoway)
print(tukeys)
  Tukey multiple comparisons of means
    95% family-wise confidence level

Fit: aov(formula = SalePrice ~ YearBuilt + Location, data = eda2.0)

$YearBuilt
                          diff         lwr        upr     p adj
1880-1889-1870-1879  62425.571 -111794.395 236645.538 0.9968518
1890-1899-1870-1879 108316.667  -69100.264 285733.597 0.7521985
1900-1909-1870-1879  21828.400 -141741.928 185398.728 1.0000000
1910-1919-1870-1879  23511.483 -132762.730 179785.696 0.9999999
1920-1929-1870-1879  15268.351 -139955.132 170491.833 1.0000000
1930-1939-1870-1879  36415.833 -120051.192 192882.859 0.9999783
1940-1949-1870-1879  18887.481 -136692.837 174467.799 1.0000000
1950-1959-1870-1879  32935.671 -121629.205 187500.546 0.9999928
1960-1969-1870-1879  45852.640 -108670.420 200375.700 0.9996021
1970-1979-1870-1879  44828.335 -109634.346 199291.016 0.9996919
1980-1989-1870-1879  82080.328  -74065.739 238226.395 0.9038411
1990-1999-1870-1879 120404.221  -34183.097 274991.539 0.3399516
2000-2009-1870-1879 134046.416  -19997.663 288090.495 0.1723779
2010-2019-1870-1879 286432.000   20306.605 552557.395 0.0211297
1890-1899-1880-1889  45891.095  -74998.165 166780.356 0.9943177
1900-1909-1880-1889 -40597.171 -140059.216  58864.873 0.9884627
1910-1919-1880-1889 -38914.089 -125857.035  48028.857 0.9732696
1920-1929-1880-1889 -47157.221 -132197.068  37882.627 0.8612030
1930-1939-1880-1889 -26009.738 -113298.776  61279.300 0.9995827
1940-1949-1880-1889 -43538.090 -129227.539  42151.359 0.9250576
1950-1959-1880-1889 -29489.901 -113321.559  54341.758 0.9974043
1960-1969-1880-1889 -16572.931 -100327.468  67181.605 0.9999972
1970-1979-1880-1889 -17597.236 -101240.323  66045.850 0.9999939
1980-1989-1880-1889  19654.756  -67057.644 106367.157 0.9999843
1990-1999-1880-1889  57978.649  -25894.380 141851.679 0.5541450
2000-2009-1880-1889  71620.845  -11246.667 154488.356 0.1810156
2010-2019-1880-1889 224006.429   -8286.861 456299.718 0.0723775
1900-1909-1890-1899 -86488.267 -191449.538  18473.005 0.2481740
1910-1919-1890-1899 -84805.184 -177989.121   8378.753 0.1217183
1920-1929-1890-1899 -93048.316 -184459.179  -1637.453 0.0411921
1930-1939-1890-1899 -71900.833 -165407.766  21606.099 0.3622895
1940-1949-1890-1899 -89429.186 -181444.684   2586.313 0.0670378
1950-1959-1890-1899 -75380.996 -165668.964  14906.972 0.2285972
1960-1969-1890-1899 -62464.027 -152680.392  27752.339 0.5512831
1970-1979-1890-1899 -63488.332 -153601.240  26624.576 0.5200630
1980-1989-1890-1899 -26236.339 -119205.208  66732.530 0.9997775
1990-1999-1890-1899  12087.554  -78238.828 102413.936 1.0000000
2000-2009-1890-1899  25729.749  -63663.733 115123.232 0.9997201
2010-2019-1890-1899 178115.333  -56585.204 412815.871 0.3855093
1910-1919-1900-1909   1683.083  -61259.201  64625.366 1.0000000
1920-1929-1900-1909  -6560.049  -66846.290  53726.191 1.0000000
1930-1939-1900-1909  14587.433  -48832.054  78006.921 0.9999812
1940-1949-1900-1909  -2940.919  -64140.076  58258.238 1.0000000
1950-1959-1900-1909  11107.271  -47462.365  69676.906 0.9999984
1960-1969-1900-1909  24024.240  -34434.956  82483.436 0.9876943
1970-1979-1900-1909  22999.935  -35299.474  81299.344 0.9916837
1980-1989-1900-1909  60251.928   -2371.515 122875.371 0.0739911
1990-1999-1900-1909  98575.821   39946.987 157204.655 0.0000014
2000-2009-1900-1909 112218.016   55036.903 169399.129 0.0000000
2010-2019-1900-1909 264603.600   40186.962 489020.238 0.0057546
1920-1929-1910-1919  -8243.132  -44309.854  27823.590 0.9999826
1930-1939-1910-1919  12904.351  -28185.903  53994.604 0.9992436
1940-1949-1910-1919  -4624.002  -42196.791  32948.787 1.0000000
1950-1959-1910-1919   9424.188  -23693.478  42541.854 0.9997548
1960-1969-1910-1919  22341.157  -10580.799  55263.113 0.5866478
1970-1979-1910-1919  21316.852  -11320.530  53954.235 0.6511550
1980-1989-1910-1919  58568.845   18718.209  98419.481 0.0000675
1990-1999-1910-1919  96892.738   63670.489 130114.987 0.0000000
2000-2009-1910-1919 110534.933   79939.880 141129.987 0.0000000
2010-2019-1910-1919 262920.517   43764.853 482076.181 0.0043523
1930-1939-1920-1929  21147.483  -15745.725  58040.690 0.8285004
1940-1949-1920-1929   3619.130  -29311.315  36549.576 1.0000000
1950-1959-1920-1929  17667.320  -10072.175  45406.815 0.6909929
1960-1969-1920-1929  30584.289    3078.745  58089.834 0.0136999
1970-1979-1920-1929  29559.985    2395.696  56724.273 0.0183244
1980-1989-1920-1929  66811.977   31304.612 102319.343 0.0000000
1990-1999-1920-1929 105135.870   77271.599 133000.142 0.0000000
2000-2009-1920-1929 118778.066   94105.062 143451.069 0.0000000
2010-2019-1920-1929 271163.649   52755.992 489571.307 0.0024255
1940-1949-1930-1939 -17528.352  -55895.197  20838.492 0.9680902
1950-1959-1930-1939  -3480.163  -37496.043  30535.717 1.0000000
1960-1969-1930-1939   9436.807  -24388.561  43262.174 0.9998055
1970-1979-1930-1939   8412.502  -25135.957  41960.960 0.9999457
1980-1989-1930-1939  45664.495    5064.328  86264.661 0.0116699
1990-1999-1930-1939  83988.388   49870.677 118106.098 0.0000000
2000-2009-1930-1939  97630.583   66065.450 129195.716 0.0000000
2010-2019-1930-1939 250016.167   30722.972 469309.362 0.0095966
1950-1959-1940-1949  14048.190  -15623.103  43719.482 0.9569770
1960-1969-1940-1949  26965.159   -2487.532  56417.850 0.1157931
1970-1979-1940-1949  25940.854   -3193.396  55075.105 0.1450676
1980-1989-1940-1949  63192.847   26156.662 100229.032 0.0000009
1990-1999-1940-1949 101516.740   71728.762 131304.718 0.0000000
2000-2009-1940-1949 115158.935   88332.398 141985.472 0.0000000
2010-2019-1940-1949 267544.519   48883.112 486205.926 0.0031277
1960-1969-1950-1959  12916.969  -10588.909  36422.848 0.8692997
1970-1979-1950-1959  11892.664  -11212.960  34998.289 0.9174826
1980-1989-1950-1959  49144.657   16637.053  81652.261 0.0000326
1990-1999-1950-1959  87468.550   63543.899 111393.202 0.0000000
2000-2009-1950-1959 101110.745   80992.882 121228.609 0.0000000
2010-2019-1950-1959 253496.329   35556.255 471436.404 0.0071106
1970-1979-1960-1969  -1024.305  -23848.531  21799.921 1.0000000
1980-1989-1960-1969  36227.688    3919.489  68535.887 0.0121836
1990-1999-1960-1969  74551.581   50898.582  98204.580 0.0000000
2000-2009-1960-1969  88193.776   68399.741 107987.811 0.0000000
2010-2019-1960-1969 240579.360   22668.939 458489.781 0.0151069
1980-1989-1970-1979  37251.993    5233.822  69270.164 0.0070810
1990-1999-1970-1979  75575.886   52320.609  98831.163 0.0000000
2000-2009-1970-1979  89218.081   69901.058 108535.104 0.0000000
2010-2019-1970-1979 241603.665   23736.056 459471.274 0.0142167
1990-1999-1980-1989  38323.893    5709.749  70938.037 0.0060710
2000-2009-1980-1989  51966.088   22032.464  81899.712 0.0000005
2010-2019-1980-1989 204351.672  -14712.632 423415.977 0.0984998
2000-2009-1990-1999  13642.195   -6647.370  33931.761 0.6025918
2010-2019-1990-1999 166027.779  -51928.212 383983.770 0.3788597
2010-2019-2000-2009 152385.584  -65185.447 369956.615 0.5306953

$Location
                  diff       lwr       upr     p adj
South-North -17988.131 -33571.23 -2405.029 0.0187573
West-North  -12861.073 -21307.63 -4414.514 0.0010641
West-South    5127.058 -11045.87 21299.985 0.7374461

Conclusion:

Location Takeaway:

Houses build in the South tend to be roughly $18,000 cheaper than houses built in the North.

Housing build in the West tend to be roughly $13,0000 cheaper than houses built in the North.

There is no statistical evidence to support price difference from West to South houses.

Year Built Takeaways

2010 - 2019 houses are showing the largest price variation from other decades ranging anywhere from $150,000 - $200,000 more than older homes.

Houses built in the 2000-2009 decade tend to be $50,000 - $115,000 more expensive than houses built in most time frames in the 1990s.

There is also significant evidence to support houses built in the 1990-1999 decade are priced anywhere from $30,000 - $100,000 higher than older homes.