Introduction

This report provides a descriptive analysis of Montgomery County food inspection data. The dataset contains 50,000 inspection records representing 1,373 unique businesses from July 23, 2026 through October 9, 2026. The purpose of this analysis is to examine common food safety violations, overall inspection outcomes, weekly failure rates, differences between passed and failed inspections, and variation in failure rates across business and inspection types.

Five different types of visualizations are used throughout the report: a bar chart, donut chart, line chart, boxplot, and heatmap. Together, these visualizations provide an overview of both the frequency and distribution of food inspection outcomes during the study period.

Data Preparation

The Montgomery County food inspection data were obtained from the Montgomery County Open Data portal. The CSV file was saved locally and loaded into R for analysis.

filename <- "Montgomery_Food_Inspections.csv"

df <- fread(filename, na.strings = c(NA, ""))

Several additional variables were created from the inspection date to support the analysis.

df$inspection_date <- ymd(substr(df$inspection_start_date, 1, 10))
df$year <- year(df$inspection_date)
df$months <- months(df$inspection_date, abbreviate = TRUE)
df$dayoftheweek <- weekdays(df$inspection_date, abbreviate = TRUE)

mymonths <- c("Jan", "Feb", "Mar", "Apr", "May", "Jun",
              "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")

df$months <- factor(df$months, levels = mymonths)

mydays <- c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")

df$dayoftheweek <- factor(df$dayoftheweek, levels = mydays)

For this analysis, a violation finding includes items identified as not in compliance, repeat violations, or problems that were corrected onsite. Therefore, a violation finding does not necessarily mean that the issue remained unresolved after the inspection.

violation_results <- c("Not In Compliance",
                       "Not In Compliance Repeat Violation",
                       "Corrected Onsite",
                       "Corrected Onsite Repeat Violation")

violation_columns <- c(
  "food_from_approved_source",
  "food_protected_from_contamination",
  "workers_restricted",
  "proper_hand_washing",
  "cooling_time_and_temperature",
  "cold_holding_temperature",
  "hot_holding_temperature",
  "cooking_time_and_temperature",
  "reheating_time_and_temperature",
  "hot_and_cold_running_water_provided",
  "proper_sewage_disposal",
  "toxic_substances_and_pesticides",
  "rodents_and_insects",
  "nutritional_labeling",
  "trans_fat_ban"
)

df$violation_count <- rowSums(
  sapply(df[, violation_columns, with = FALSE],
         function(x) x %in% violation_results)
)

Descriptive Statistics

The dataset contains 50,000 inspection records from 1,373 unique registered businesses. Inspection records in this extract range from July 23, 2026 through October 9, 2026.

The average inspection contained approximately 0.63 violation findings, while the median was zero. The first and third quartiles were zero and one, respectively, indicating that most inspections contained relatively few violation findings.

total_inspections <- nrow(df)

unique_businesses <- length(unique(na.omit(df$registration_number)))

date_range <- range(df$inspection_date, na.rm = TRUE)

status_summary <- df %>%
  dplyr::filter(!is.na(status)) %>%
  dplyr::group_by(status) %>%
  dplyr::summarise(n = dplyr::n(), .groups = "drop") %>%
  dplyr::mutate(percent = round(n / sum(n) * 100, 2)) %>%
  data.frame()

summary(df$violation_count)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.0000  0.0000  0.0000  0.6335  1.0000  5.0000
status_summary
##       status     n percent
## 1  Cancelled    54    0.11
## 2     Closed   190    0.38
## 3       Fail  2172    4.34
## 4 Incomplete   117    0.23
## 5       Pass 47467   94.93

Overall, 94.9% of inspections resulted in a passing outcome, while 4.3% resulted in failure. Cancelled, closed, and incomplete inspections each represented less than 1% of the total records. These results indicate that the large majority of inspections in the dataset ended with a passing outcome.

Visualization 1: Most Common Food Safety Violations

The first visualization examines which food safety problems were identified most frequently during the inspection period. A violation finding includes issues recorded as not in compliance, repeat violations, and problems that were corrected onsite.

violations_df <- df %>%
  dplyr::select(dplyr::all_of(violation_columns)) %>%
  tidyr::pivot_longer(cols = everything(), names_to = "Violation", values_to = "Result")

out_of_compliance <- violations_df %>%
  dplyr::filter(Result %in% violation_results)

violation_count <- out_of_compliance %>%
  dplyr::group_by(Violation) %>%
  dplyr::summarise(n = dplyr::n(), .groups = "drop") %>%
  dplyr::arrange(dplyr::desc(n)) %>%
  data.frame()

top10_violations <- violation_count[1:10, ]

top10_violations$Violation <- gsub("_", " ", top10_violations$Violation)
top10_violations$Violation <- tools::toTitleCase(top10_violations$Violation)

ggplot(top10_violations, aes(x = reorder(Violation, n), y = n)) +
  geom_bar(stat = "identity", color = "black", fill = "steelblue") +
  geom_text(aes(label = comma(n)), hjust = -0.15, size = 3.5) +
  coord_flip() +
  labs(title = "Top 10 Food Safety Violations in Montgomery County",
       subtitle = "Violation Findings Identified During Food Inspections",
       x = "", y = "Number of Violation Findings") +
  scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.15))) +
  theme_light() +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5))

Rodents and insects were the most frequently identified violation category, with 10,526 findings, followed by cold holding temperature with 9,416. These two categories occurred substantially more often than the remaining violations. Toxic substances and pesticides ranked third with 2,589 findings, followed by food protected from contamination with 2,250 and hot holding temperature with 2,155. The results suggest that pest control and proper temperature management were among the most common food safety concerns identified during the study period.

Visualization 2: Inspection Outcomes

The second visualization shows the distribution of inspection outcomes in the dataset. A donut chart is used to show how the 50,000 inspection records were divided among passing, failing, closed, cancelled, and incomplete outcomes.

status_summary$status_label <- paste0(
  status_summary$status, " (",
  round(status_summary$percent, 1), "%)"
)

ggplot(status_summary, aes(x = 2, y = n, fill = status_label)) +
  geom_bar(stat = "identity", color = "white") +
  coord_polar(theta = "y") +
  xlim(0.5, 2.5) +
  geom_text(aes(label = ifelse(percent >= 1,
                               paste0(round(percent, 1), "%"), "")),
            position = position_stack(vjust = 0.5),
            size = 5,
            fontface = "bold") +
  labs(title = "Montgomery County Food Inspection Outcomes",
       subtitle = "Distribution of Inspection Results",
       fill = "Inspection Outcome") +
  scale_fill_brewer(palette = "Set2") +
  theme_void() +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5),
        legend.title = element_text(face = "bold"),
        legend.text = element_text(size = 10))

The results show that passing inspections made up the overwhelming majority of records, accounting for 94.9% of all inspections. Failed inspections represented 4.3%, while closed, incomplete, and cancelled inspections each represented less than 1%. This shows that although food safety violations were identified throughout the dataset, most inspections still resulted in an overall passing outcome.

Visualization 3: Weekly Food Inspection Failure Rate

The third visualization examines how the percentage of failed inspections changed from week to week during the study period. Only inspections with a final status of Pass or Fail were included in this calculation. The first and last partial weeks were excluded so that the weekly comparisons were based on complete weeks.

weekly_failure <- df %>%
  dplyr::filter(status %in% c("Pass", "Fail")) %>%
  dplyr::mutate(week = floor_date(inspection_date, "week", week_start = 1)) %>%
  dplyr::group_by(week) %>%
  dplyr::summarise(total_inspections = dplyr::n(),
                   failed_inspections = sum(status == "Fail"),
                   .groups = "drop") %>%
  dplyr::mutate(failure_rate = failed_inspections / total_inspections * 100) %>%
  data.frame()

weekly_failure <- weekly_failure %>%
  dplyr::filter(week != min(week), week != max(week))

ggplot(weekly_failure, aes(x = week, y = failure_rate)) +
  geom_line(color = "steelblue", size = 1.5) +
  geom_point(shape = 21, size = 4, color = "black", fill = "white") +
  labs(title = "Weekly Food Inspection Failure Rate",
       subtitle = "Percentage of Pass/Fail Inspections Resulting in Failure During the Study Period",
       x = "Week", y = "Failure Rate") +
  scale_x_date(date_breaks = "1 week", date_labels = "%b %d") +
  scale_y_continuous(labels = scales::percent_format(scale = 1)) +
  theme_light() +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5),
        axis.text.x = element_text(angle = 45, hjust = 1))

Weekly failure rates generally ranged from about 3.2% to 5.2% from late July through mid-September. The rate then increased noticeably during the final two complete weeks, reaching 8.1% for the week of September 21 and 8.4% for the week of September 28. This suggests a short-term increase in the proportion of failed inspections near the end of the study period. However, because the dataset covers only a limited period of time, the pattern should not be interpreted as evidence of a long-term trend.

Visualization 4: Violations by Inspection Outcome

The fourth visualization compares the number of violation findings for inspections that passed with those that failed. A boxplot is used to summarize and compare the distribution of violation findings between the two inspection outcomes.

boxplot_df <- df %>%
  dplyr::filter(status %in% c("Pass", "Fail"))

boxplot_df$status <- factor(boxplot_df$status, levels = c("Pass", "Fail"))

ggplot(boxplot_df, aes(x = status, y = violation_count, fill = status)) +
  geom_boxplot() +
  labs(title = "Food Safety Violations by Inspection Outcome",
       subtitle = "Distribution of Violation Findings for Passed and Failed Inspections",
       x = "Inspection Outcome",
       y = "Number of Violation Findings") +
  scale_fill_brewer(palette = "Set1") +
  theme_light() +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5),
        legend.position = "none")

A boxplot summarizes the distribution of the data. The line inside each box represents the median, while the box represents the middle 50% of observations. The whiskers show the broader spread of the data, and points beyond the whiskers represent unusually high values.

The visualization shows that failed inspections generally had more violation findings than inspections that passed. Passed inspections were concentrated near zero violations, while failed inspections had a higher median and a wider distribution. This indicates a clear relationship between the number of food safety problems identified during an inspection and the likelihood of receiving a failing outcome.

Visualization 5: Failure Rate by Business and Inspection Type

The fifth visualization examines how inspection failure rates differed across common business subtypes and inspection types. To keep the heatmap readable, the analysis focuses on the six most common business subtypes and the five most common inspection types in the dataset.

business_count <- df %>%
  dplyr::filter(status %in% c("Pass", "Fail"), !is.na(business_subtype)) %>%
  dplyr::group_by(business_subtype) %>%
  dplyr::summarise(n = dplyr::n(), .groups = "drop") %>%
  dplyr::arrange(dplyr::desc(n))

top_business <- business_count$business_subtype[1:6]

inspection_count <- df %>%
  dplyr::filter(status %in% c("Pass", "Fail"), !is.na(inspection_type)) %>%
  dplyr::group_by(inspection_type) %>%
  dplyr::summarise(n = dplyr::n(), .groups = "drop") %>%
  dplyr::arrange(dplyr::desc(n))

top_inspection <- inspection_count$inspection_type[1:5]

heatmap_df <- df %>%
  dplyr::filter(status %in% c("Pass", "Fail"),
                business_subtype %in% top_business,
                inspection_type %in% top_inspection) %>%
  dplyr::group_by(business_subtype, inspection_type) %>%
  dplyr::summarise(total_inspections = dplyr::n(),
                   failed_inspections = sum(status == "Fail"),
                   .groups = "drop") %>%
  dplyr::mutate(failure_rate = failed_inspections / total_inspections * 100) %>%
  data.frame()

ggplot(heatmap_df, aes(x = inspection_type, y = business_subtype, fill = failure_rate)) +
  geom_tile(color = "white") +
  geom_text(aes(label = paste0(round(failure_rate, 1), "%")), size = 3.5) +
  labs(title = "Food Inspection Failure Rate by Business and Inspection Type",
       subtitle = "Failure Rate Among the Most Common Business Subtypes and Inspection Types",
       x = "Inspection Type",
       y = "Business Subtype",
       fill = "Failure Rate (%)") +
  scale_fill_gradientn(colors = c("steelblue", "skyblue", "gold", "orange", "firebrick"),
                       values = scales::rescale(c(0, 3, 8, 20, 51)),
                       breaks = c(0, 5, 10, 20, 30, 40, 50),
                       na.value = "grey90") +
  theme_light() +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5),
        axis.text.x = element_text(angle = 45, hjust = 1),
        panel.grid = element_blank())

The heatmap shows substantial differences in failure rates across business subtype and inspection type combinations. One of the highest observed failure rates was for Food License-Mobile Food-New-High businesses during pre-opening inspections, at 50.8%. Food Service Facility License-Low businesses also showed relatively high failure rates during reinspections at 35.6% and monitoring inspections at 26.2%. In addition, Food License-Mobile Food-New-Moderate businesses had a 29.3% failure rate for annual inspections.

These results suggest that failure rates can vary considerably depending on both the type of food business and the type of inspection being conducted. However, the number of inspections within each combination is not equal, so percentages based on smaller groups should be interpreted cautiously.

Conclusion

This analysis examined 50,000 Montgomery County food inspection records from 1,373 unique businesses between July 23, 2026 and October 9, 2026. Overall, the large majority of inspections resulted in a passing outcome, with 94.9% passing and 4.3% failing.

The analysis also identified several common food safety concerns. Rodents and insects were the most frequent violation finding, followed by cold holding temperature. These two categories appeared much more often than the other violations in the dataset.

Weekly failure rates were generally between approximately 3% and 5% for much of the study period before increasing to more than 8% during the final two complete weeks. Failed inspections also tended to contain more violation findings than passed inspections, which supports the relationship between the number of identified food safety problems and the final inspection outcome.

The heatmap showed that failure rates also varied by business subtype and inspection type. Some combinations had noticeably higher failure rates than others, including certain mobile food businesses and pre-opening, monitoring, and reinspection categories.

Overall, the visualizations show that food inspection outcomes vary across several factors, including the type of violation identified, the number of violation findings, the timing of inspections, and the type of business and inspection being performed.

Limitations

There are several limitations to this analysis. First, the dataset used for this project contains a 50,000-record extract covering only July 23, 2026 through October 9, 2026. Because the study period is relatively short, the weekly failure-rate visualization should not be interpreted as evidence of a long-term or seasonal trend.

Second, the number of inspections differed across business subtype and inspection type combinations. As a result, some failure-rate percentages are based on fewer observations than others and should be interpreted with caution.

Finally, this analysis defines a violation finding as an issue recorded as not in compliance, a repeat violation, or a problem corrected onsite. Therefore, the violation counts represent problems identified during an inspection and do not necessarily indicate that all issues remained unresolved after the inspection.

Despite these limitations, the dataset provides a useful descriptive view of food inspection patterns in Montgomery County during the study period.