Introduction

This report analyzes a Census Bureau American Community Survey (ACS) Public Use Microdata Sample (PUMS) housing file for Idaho, drawn from the 2006 single-year ACS. Each of the 6,496 rows is one sampled housing unit — not a summary statistic — with variables covering tenure (own/rent), structure type, rooms and bedrooms, household income, monthly housing costs, vehicles, and the year the structure was built.

About the file’s structure

This is genuine microdata, and its shape reflects that:

  • WGTP is the housing unit’s sampling weight — the number of actual Idaho housing units that this one sampled record represents. Summing WGTP across all rows, rather than simply counting rows, is what turns a 6,496-row sample into a population-representative estimate.
  • wgtp1 through wgtp80 are 80 replicate weights, a standard Census technique for computing margins of error on weighted estimates without requiring a closed-form formula for every statistic. They are not used for point estimates and are excluded from the substantive analysis below, but they explain the bulk of this file’s 188 columns.
  • Categorical variables are coded, not spelled out — tenure, structure type, and year-built all arrive as integer codes that must be joined against the Census Bureau’s PUMS data dictionary before they mean anything. That decoding happens in the setup step below, using the 2006 ACS PUMS Housing Record definitions.
raw <- read_csv("idaho_housing.csv", col_types = cols(.default = "c"))

housing <- raw %>%
  transmute(
    WGTP = as.numeric(WGTP),
    NP   = as.numeric(NP),
    TEN  = TEN,
    BLD  = BLD,
    BDS  = as.numeric(BDS),
    RMS  = as.numeric(RMS),
    VEH  = as.numeric(VEH),
    YBL  = YBL,
    HINCP  = as.numeric(HINCP),
    GRNTP  = as.numeric(GRNTP),
    SMOCP  = as.numeric(SMOCP),
    GRPIP  = as.numeric(GRPIP),
    OCPIP  = as.numeric(OCPIP)
  ) %>%
  mutate(
    Tenure = case_when(
      TEN == "1" ~ "Owned with mortgage/loan",
      TEN == "2" ~ "Owned free and clear",
      TEN == "3" ~ "Rented",
      TEN == "4" ~ "Occupied without payment of rent",
      TRUE ~ NA_character_
    ),
    Structure = case_when(
      BLD == "01" ~ "Mobile home or trailer",
      BLD == "02" ~ "One-family house, detached",
      BLD == "03" ~ "One-family house, attached",
      BLD == "04" ~ "2 apartments",
      BLD == "05" ~ "3-4 apartments",
      BLD == "06" ~ "5-9 apartments",
      BLD == "07" ~ "10-19 apartments",
      BLD == "08" ~ "20-49 apartments",
      BLD == "09" ~ "50+ apartments",
      BLD == "10" ~ "Boat, RV, van, etc.",
      TRUE ~ NA_character_
    ),
    YearBuilt = case_when(
      YBL == "1" ~ "1939 or earlier",
      YBL == "2" ~ "1940-1949",
      YBL == "3" ~ "1950-1959",
      YBL == "4" ~ "1960-1969",
      YBL == "5" ~ "1970-1979",
      YBL == "6" ~ "1980-1989",
      YBL == "7" ~ "1990-1999",
      YBL == "8" ~ "2000-2004",
      YBL == "9" ~ "2005",
      TRUE ~ NA_character_
    ),
    YearBuilt = factor(YearBuilt, levels = c("1939 or earlier","1940-1949","1950-1959","1960-1969",
                                              "1970-1979","1980-1989","1990-1999","2000-2004","2005"))
  )

About the data

tibble(
  Metric = c("Sampled housing records", "Estimated total Idaho housing units (weighted)",
             "Records with tenure recorded", "Records with household income recorded"),
  Value = c(
    comma(nrow(housing)),
    comma(sum(housing$WGTP, na.rm = TRUE)),
    comma(sum(!is.na(housing$Tenure))),
    comma(sum(!is.na(housing$HINCP)))
  )
) %>%
  kable(caption = "Dataset snapshot") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Dataset snapshot
Metric Value
Sampled housing records 6,496
Estimated total Idaho housing units (weighted) 615,703
Records with tenure recorded 5,637
Records with household income recorded 5,637

The gap between “sampled records” (6,496) and “records with tenure recorded” (5,637) is expected, not a data-quality problem: PUMS housing files include some vacant units and non-interviews for which occupancy-related fields are legitimately blank. All weighted estimates below use WGTP and are therefore representative of occupied and applicable Idaho housing units, not just the raw row count.


Tenure: who owns, who rents

tenure_summary <- housing %>%
  filter(!is.na(Tenure)) %>%
  group_by(Tenure) %>%
  summarise(units = sum(WGTP)) %>%
  mutate(share = units / sum(units))

ggplot(tenure_summary, aes(x = fct_reorder(Tenure, units), y = units, fill = Tenure)) +
  geom_col(show.legend = FALSE) +
  geom_text(aes(label = paste0(comma(units), " (", percent(share, accuracy = 0.1), ")")),
            hjust = -0.05, size = 3.5) +
  coord_flip(clip = "off") +
  scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.35))) +
  scale_fill_brewer(palette = "Blues") +
  labs(title = "Idaho housing units by tenure, 2006 (weighted estimate)",
       x = NULL, y = "Estimated housing units") +
  theme_minimal(base_size = 12)

Homeownership dominates Idaho’s housing stock: roughly 74% of housing units are owner-occupied (with or without a mortgage), versus about 24% rented — a notably higher ownership rate than the U.S. national average for this period, consistent with Idaho’s larger share of single-family, non-metro housing.


What kind of housing stock is it?

structure_summary <- housing %>%
  filter(!is.na(Structure)) %>%
  group_by(Structure) %>%
  summarise(units = sum(WGTP)) %>%
  mutate(share = units / sum(units))

ggplot(structure_summary, aes(x = fct_reorder(Structure, units), y = units)) +
  geom_col(fill = "#2c7fb8") +
  geom_text(aes(label = percent(share, accuracy = 0.1)), hjust = -0.1, size = 3.3) +
  coord_flip(clip = "off") +
  scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.2))) +
  labs(title = "Idaho housing units by structure type, 2006 (weighted estimate)",
       x = NULL, y = "Estimated housing units") +
  theme_minimal(base_size = 12)

Detached single-family homes are the overwhelming majority of Idaho’s housing stock (about 72%), more than seven times the share of any other category. Mobile homes and trailers (10.5%) outnumber every size class of apartment building individually, and large apartment complexes (20+ units) are rare — together barely over 2% of the state’s housing units. This is a housing landscape defined by low-density, single-family construction rather than multifamily density.


Household income and housing costs

inc_by_tenure <- housing %>%
  filter(!is.na(Tenure), !is.na(HINCP)) %>%
  group_by(Tenure) %>%
  summarise(
    weighted_mean_income = weighted.mean(HINCP, WGTP),
    weighted_median_income = matrixStats::weightedMedian(HINCP, WGTP)
  )

inc_by_tenure %>%
  mutate(across(where(is.numeric), ~ dollar(round(.)))) %>%
  kable(col.names = c("Tenure", "Weighted Mean Income", "Weighted Median Income"),
        caption = "Household income by tenure (weighted)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Household income by tenure (weighted)
Tenure Weighted Mean Income Weighted Median Income
Occupied without payment of rent $31,176 $24,852
Owned free and clear $48,546 $34,000
Owned with mortgage/loan $67,540 $57,000
Rented $34,118 $28,000
ggplot(housing %>% filter(!is.na(Tenure), !is.na(HINCP), HINCP >= 0),
       aes(x = HINCP, weight = WGTP, fill = Tenure)) +
  geom_density(alpha = 0.5, color = NA) +
  scale_x_continuous(labels = dollar, limits = c(0, 200000)) +
  labs(title = "Household income distribution by tenure (weighted)",
       x = "Household income (2006 $)", y = "Density", fill = NULL) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "bottom")

Homeowners have substantially higher household incomes than renters: households owning with a mortgage report a weighted mean income of roughly $67,500, versus about $34,100 for renters — nearly double. This is the expected and well-documented pattern (mortgage qualification itself selects for higher, more stable income), but the gap is large enough to be worth stating plainly rather than assuming.

cost_burden <- tibble(
  Group = c("Renters (Gross Rent / Income)", "Owners (Selected Monthly Costs / Income)"),
  `Weighted Median % of Income` = c(
    matrixStats::weightedMedian(housing$GRPIP[!is.na(housing$GRPIP)], housing$WGTP[!is.na(housing$GRPIP)]),
    matrixStats::weightedMedian(housing$OCPIP[!is.na(housing$OCPIP)], housing$WGTP[!is.na(housing$OCPIP)])
  )
)

cost_burden %>%
  kable(caption = "Housing cost burden: share of household income spent on housing (weighted median)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Housing cost burden: share of household income spent on housing (weighted median)
Group Weighted Median % of Income
Renters (Gross Rent / Income) 26
Owners (Selected Monthly Costs / Income) 20

Renters carry a meaningfully heavier cost burden than owners: the typical (median) renter spends about 26% of household income on gross rent, compared to about 20% for owners on their mortgage/tax/insurance costs. Federal housing policy typically treats 30%+ of income spent on housing as “cost-burdened” — worth keeping in mind when reading the income distributions above, since a lower income combined with a higher cost share compounds for renting households.


Household size, rooms, and vehicles

size_summary <- housing %>%
  filter(!is.na(BDS)) %>%
  summarise(
    `Weighted mean bedrooms` = weighted.mean(BDS, WGTP),
    `Weighted mean rooms` = weighted.mean(RMS, WGTP, na.rm = TRUE),
    `Weighted mean household size (persons)` = weighted.mean(NP, WGTP, na.rm = TRUE),
    `Weighted mean vehicles available` = weighted.mean(VEH, WGTP, na.rm = TRUE)
  )

size_summary %>%
  mutate(across(everything(), ~ round(., 2))) %>%
  pivot_longer(everything(), names_to = "Measure", values_to = "Weighted Mean") %>%
  kable(caption = "Household and structure size (weighted averages)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Household and structure size (weighted averages)
Measure Weighted Mean
Weighted mean bedrooms 2.87
Weighted mean rooms 5.68
Weighted mean household size (persons) 2.30
Weighted mean vehicles available 2.10
veh_summary <- housing %>%
  filter(!is.na(VEH)) %>%
  group_by(VEH) %>%
  summarise(units = sum(WGTP)) %>%
  mutate(share = units / sum(units))

ggplot(veh_summary, aes(x = factor(VEH), y = units)) +
  geom_col(fill = "#31a354") +
  geom_text(aes(label = percent(share, accuracy = 0.1)), vjust = -0.4, size = 3.5) +
  scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.15))) +
  labs(title = "Vehicles available per household, 2006 (weighted estimate)",
       x = "Number of vehicles available", y = "Estimated housing units") +
  theme_minimal(base_size = 12)

The typical Idaho home has just under 3 bedrooms and about 6 rooms overall, consistent with the detached-single-family-dominated stock seen above. Two-vehicle households are the single most common arrangement (about 37% of households), and only about 3% of households report having no vehicle at all — a strong signal of Idaho’s car-dependent, low-density settlement pattern, in contrast to denser urban states where zero-vehicle households are far more common.


When was the housing stock built?

ybl_summary <- housing %>%
  filter(!is.na(YearBuilt)) %>%
  group_by(YearBuilt) %>%
  summarise(units = sum(WGTP))

ggplot(ybl_summary, aes(x = YearBuilt, y = units)) +
  geom_col(fill = "#e34a33") +
  geom_text(aes(label = comma(units)), vjust = -0.4, size = 3) +
  scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.15))) +
  labs(title = "Idaho housing units by decade built (weighted estimate)",
       x = NULL, y = "Estimated housing units") +
  theme_minimal(base_size = 12) +
  theme(axis.text.x = element_text(angle = 30, hjust = 1))

Idaho’s housing stock shows two clear building booms: the 1950s and the 1970s each added more housing than any other decade on record, together accounting for well over 40% of the state’s pre-2006 housing stock. Construction slowed markedly in the 1980s before picking back up through the 1990s and early 2000s — a pattern that roughly tracks Idaho’s population growth waves over the second half of the 20th century.


Explore the data

housing %>%
  select(WGTP, NP, Tenure, Structure, BDS, RMS, VEH, YearBuilt, HINCP) %>%
  rename(Weight = WGTP, Persons = NP, Bedrooms = BDS, Rooms = RMS, Vehicles = VEH,
         `Household Income` = HINCP) %>%
  datatable(options = list(pageLength = 10, scrollX = TRUE), rownames = FALSE, filter = "top")

Key takeaways

  • Idaho is a homeowning state: roughly 3 in 4 housing units are owner-occupied, well above typical national rates for this period.
  • The housing stock is overwhelmingly single-family and low-density: detached houses make up about 72% of all units, with mobile homes (10.5%) outnumbering apartment buildings of any single size class.
  • Owners out-earn renters by nearly 2-to-1 on a weighted median basis ($67,500 vs. $34,100), and renters devote a larger share of income to housing (26% vs. 20% median), compounding the income gap into a larger relative cost burden.
  • Vehicle ownership is nearly universal: only about 3% of households report zero vehicles, and two-car households are the norm — consistent with a rural, low-transit housing landscape.
  • Idaho’s housing stock was built in two major waves, the 1950s and 1970s, together accounting for the largest shares of pre-2006 construction.

A note on weighting and precision

Every estimate above uses the WGTP housing-unit weight, which is what makes a 6,496-row sample representative of the full Idaho housing stock. This file also carries 80 replicate weights (wgtp1-wgtp80) specifically so that a margin of error can be computed for any of these estimates using the Census Bureau’s successive-difference replication method — a step not performed here, since it requires specialized survey-design software (e.g. R’s survey package with svrepdesign()). Any estimate in this report should be read as a point estimate with an unstated sampling margin of error, not an exact population count.

Limitations

This is a single-year (2006) survey sample, not a census — every figure here is subject to sampling error, and Idaho’s relatively small population means single-year state-level ACS estimates carry wider margins of error than larger states would. The file also reports nominal dollar figures with no adjustment for inflation, so income and cost figures should not be compared directly to other years without adjustment. Codes for VAL (owner-estimated home value) were intentionally left undecoded in this report, since that variable uses a bracketed range code whose exact dollar cutoffs are best confirmed against the specific-year Census PUMS data dictionary before publishing precise figures.


Report generated in R Markdown. To publish: put idaho_housing.csv in the same folder as this file, open it in RStudio, click Knit, then use the Publish button (top right of the preview pane) to push directly to RPubs. This report uses matrixStats::weightedMedian() for weighted medians — install it with install.packages("matrixStats") if you don’t already have it.