16th Biggest City per US State

Down the rabbit hole of US Census Bureau Data
Author

Mara Alexeev

Published

July 11, 2025

Modified

July 11, 2025

Marshalltown, Iowa

Marshalltown, Iowa is a city in Central Iowa that is probably best known for things you might see in the stores like Lowe’s and Home Depot: Lennox and the Marshalltown Company. Lennox makes all sorts of AC and heating applicances, and the Marshalltown Company, which makes an incredible number of specialized tools used in everything from tiling your bathroom to archaeological expeditions. I think many of these big “handyman” stores will have part of an aisle dedicated to these tools. In a story that feels like it could have only ever happened in 19th century America, a single man and local blacksmith, David Lennox, was a pivotal person in both companies.

I grew up near Marshalltown, and I attended school in Marshalltown until leaving for college in 2004. A friend from medical school messaged me today to tell me that she happened upon the trivia that Marshalltown is the 16th largest city in Iowa. I didn’t know how to put in that in perspective. I guess that it is probably still under 30,000 people. It seems to be growing slightly when I go visit my parents, and I feel like it was in the 25,000 range when I moved away 21(!) years ago.

Census Bureau

So I had to head over to the Census Bureau’s website to see what the 16th largest cities in other states look like around the United States.

Code
library(tidyverse)
library(crosstalk)
library(DT)
Code
# The path to the local data file
# The file is in a subfolder called "data"
file_path <- "data/sub-est2024.csv"

# Read the data from the CSV file
# The 'tryCatch' block will show a helpful message if the file is not found.
local_data <- tryCatch({
  # Explicitly set the SUMLEV column to be read as character to preserve leading zeros
  # Added locale to handle "invalid UTF-8" warnings by specifying the file encoding.
  read_csv(file_path, 
           col_types = cols(SUMLEV = col_character()),
           locale = locale(encoding = "latin1"))
}, error = function(e) {
  message("Error reading file: ", e$message)
  message("\nPlease make sure '", file_path, "' exists and is a valid CSV file.")
  # Return an empty tibble or NULL if the file can't be read
  tibble() 
})
Code
 local_data <- local_data %>%
    mutate(
      sumlev_desc = case_when(
        SUMLEV == "040" ~ "State",
        SUMLEV == "050" ~ "County",
        SUMLEV == "061" ~ "Minor Civil Division",
        SUMLEV == "071" ~ "Minor Civil Division place part",
        SUMLEV == "157" ~ "County place part",
        SUMLEV == "162" ~ "Incorporated place",
        SUMLEV == "170" ~ "Consolidated city",
        SUMLEV == "172" ~ "Consolidated city -- place within consolidated city",
        TRUE ~ "Other" # A default for any values not in the key
      )
    )

But before we get to granular level data, let’s just look at the state level. Who is #16???

Code
  ranked_states <- local_data %>%
    filter(SUMLEV == "040") %>%
    arrange(desc(ESTIMATESBASE2020)) %>%
    mutate(Rank = row_number()) %>%
    select(Rank, STNAME, ESTIMATESBASE2020)

  # Assign the 16th ranked state name to a variable
  state_16 <- ranked_states$STNAME[16]

  # Display the table
  knitr::kable(
    ranked_states,
    caption = "State Population for 2020 Census, in Descending Order",
    col.names = c("Rank", "State Name", "2020 Population Base"),
    format.args = list(big.mark = ","))
State Population for 2020 Census, in Descending Order
Rank State Name 2020 Population Base
1 California 39,555,674
2 Texas 29,149,458
3 Florida 21,538,192
4 New York 20,203,772
5 Pennsylvania 13,002,909
6 Illinois 12,821,814
7 Ohio 11,799,453
8 Georgia 10,713,755
9 North Carolina 10,441,499
10 Michigan 10,079,338
11 New Jersey 9,289,014
12 Virginia 8,631,388
13 Washington 7,707,586
14 Arizona 7,158,110
15 Massachusetts 7,033,132
16 Tennessee 6,912,347
17 Indiana 6,786,587
18 Maryland 6,181,629
19 Missouri 6,154,854
20 Wisconsin 5,894,170
21 Colorado 5,775,324
22 Minnesota 5,706,692
23 South Carolina 5,118,252
24 Alabama 5,025,369
25 Louisiana 4,657,874
26 Kentucky 4,506,302
27 Oregon 4,237,224
28 Oklahoma 3,959,405
29 Connecticut 3,607,701
30 Utah 3,271,608
31 Iowa 3,190,546
32 Nevada 3,105,595
33 Arkansas 3,011,553
34 Mississippi 2,961,278
35 Kansas 2,937,745
36 New Mexico 2,117,555
37 Nebraska 1,961,996
38 Idaho 1,839,140
39 West Virginia 1,793,736
40 Hawaii 1,455,252
41 New Hampshire 1,377,546
42 Maine 1,363,196
43 Rhode Island 1,097,354
44 Montana 1,084,216
45 Delaware 989,955
46 South Dakota 886,729
47 North Dakota 779,046
48 Alaska 733,395
49 District of Columbia 689,545
50 Vermont 643,082
51 Wyoming 576,844

So our 16th largest state is Tennessee!

But let’s see what the 16th largest “city” is in each state. I will need to get some better understanding of how the Census Bureau defines their cities to make sure I am filtering on the correct codes, but I want to get this out quickly for my curious friends! Marshalltown does come out as the 16th based on 2020 population estimates.

Code
  ranked_cities_by_state_all <- local_data %>%
    distinct(STNAME, NAME, ESTIMATESBASE2020, .keep_all = TRUE) %>%
    filter(SUMLEV %in% c( "162", "61", "50")) %>%
    group_by(STNAME) %>%
    arrange(desc(ESTIMATESBASE2020)) %>%
    mutate(Rank = row_number()) %>%
    filter(Rank == 16 ) %>%
    ungroup() %>%
    select(STNAME, NAME, ESTIMATESBASE2020) %>%
    arrange(STNAME)

    ranked_cities_by_state_all %>%
      mutate(Order = row_number()) %>%
  relocate(Order) %>%
      
    knitr::kable(
    caption = "16th Largest City or Place by 2020 Population Within Each State order alphabetically by State Name",
    col.names = c("Order", "State Name", "Place Name", "2020 Census Population"),
    format.args = list(big.mark = ",")
  )
16th Largest City or Place by 2020 Population Within Each State order alphabetically by State Name
Order State Name Place Name 2020 Census Population
1 Alabama Alabaster city 33,342
2 Alaska Nome city 3,695
3 Arizona Queen Creek town 59,489
4 Arkansas Jacksonville city 29,483
5 California Santa Clarita city 232,809
6 Colorado Castle Rock town 73,158
7 Connecticut Torrington city 35,503
8 Delaware Milton town 3,315
9 Florida Palm Bay city 119,751
10 Georgia Stonecrest city 59,189
11 Idaho Mountain Home city 16,064
12 Illinois Bolingbrook village 73,949
13 Indiana Anderson city 54,848
14 Iowa Marshalltown city 27,592
15 Kansas Derby city 25,660
16 Kentucky Paducah city 27,130
17 Louisiana Hammond city 19,632
18 Maine Old Town city 7,438
19 Maryland Easton town 17,103
20 Massachusetts Malden city 66,274
21 Michigan Kalamazoo city 73,547
22 Minnesota Coon Rapids city 63,636
23 Mississippi Brandon city 25,097
24 Missouri Jefferson City city 43,237
25 Montana Lewistown city 5,955
26 Nebraska Lexington city 10,663
27 Nevada Carlin city 2,051
28 New Jersey Plainfield city 54,600
29 New Mexico Artesia city 12,875
30 New York Freeport village 44,472
31 North Carolina Chapel Hill town 62,264
32 North Dakota Horace city 3,206
33 Ohio Middletown city 50,986
34 Oklahoma Jenks city 25,946
35 Oregon Oregon City city 37,585
36 Pennsylvania Chester city 32,724
37 South Carolina Anderson city 29,441
38 South Dakota Madison city 6,193
39 Tennessee Cleveland city 47,799
40 Texas Grand Prairie city 196,075
41 Utah Draper city 51,025
42 Vermont Enosburg Falls village 1,351
43 Virginia Manassas city 42,773
44 Washington Pasco city 77,345
45 West Virginia Oak Hill city 8,167
46 Wisconsin Brookfield city 41,465
47 Wyoming Torrington city 6,128
Code
    ranked_cities_by_state_by_pop <-ranked_cities_by_state_all %>% 
      arrange(desc(ESTIMATESBASE2020)) %>% mutate(Order = row_number()) %>%
  relocate(Order) 
    

    knitr::kable(ranked_cities_by_state_by_pop ,
    caption = "16th Largest City or Place by 2020 Census Population Within Each State ordered by Population Size",
    col.names = c("Order", "State Name", "Place Name", "2020 Census Population"),
    format.args = list(big.mark = ",")
  )
16th Largest City or Place by 2020 Census Population Within Each State ordered by Population Size
Order State Name Place Name 2020 Census Population
1 California Santa Clarita city 232,809
2 Texas Grand Prairie city 196,075
3 Florida Palm Bay city 119,751
4 Washington Pasco city 77,345
5 Illinois Bolingbrook village 73,949
6 Michigan Kalamazoo city 73,547
7 Colorado Castle Rock town 73,158
8 Massachusetts Malden city 66,274
9 Minnesota Coon Rapids city 63,636
10 North Carolina Chapel Hill town 62,264
11 Arizona Queen Creek town 59,489
12 Georgia Stonecrest city 59,189
13 Indiana Anderson city 54,848
14 New Jersey Plainfield city 54,600
15 Utah Draper city 51,025
16 Ohio Middletown city 50,986
17 Tennessee Cleveland city 47,799
18 New York Freeport village 44,472
19 Missouri Jefferson City city 43,237
20 Virginia Manassas city 42,773
21 Wisconsin Brookfield city 41,465
22 Oregon Oregon City city 37,585
23 Connecticut Torrington city 35,503
24 Alabama Alabaster city 33,342
25 Pennsylvania Chester city 32,724
26 Arkansas Jacksonville city 29,483
27 South Carolina Anderson city 29,441
28 Iowa Marshalltown city 27,592
29 Kentucky Paducah city 27,130
30 Oklahoma Jenks city 25,946
31 Kansas Derby city 25,660
32 Mississippi Brandon city 25,097
33 Louisiana Hammond city 19,632
34 Maryland Easton town 17,103
35 Idaho Mountain Home city 16,064
36 New Mexico Artesia city 12,875
37 Nebraska Lexington city 10,663
38 West Virginia Oak Hill city 8,167
39 Maine Old Town city 7,438
40 South Dakota Madison city 6,193
41 Wyoming Torrington city 6,128
42 Montana Lewistown city 5,955
43 Alaska Nome city 3,695
44 Delaware Milton town 3,315
45 North Dakota Horace city 3,206
46 Nevada Carlin city 2,051
47 Vermont Enosburg Falls village 1,351

Let’s see what happened over the past 5 years in Iowa with the top 25 cities. Did Marshalltown hold on in 2024???

Code
  ranked_cities_by_iowa <- local_data %>%
  filter(STNAME == "Iowa") %>%
filter(SUMLEV %in% c( "162", "61", "50")) %>%
    arrange(desc(POPESTIMATE2024)) %>%
    mutate(Rank = row_number()) %>%
    filter(Rank <= 25 ) %>%
   
    select(
      NAME,
      Rank,
      ESTIMATESBASE2020,
    
      POPESTIMATE2021,
      POPESTIMATE2022,
      POPESTIMATE2023,
      POPESTIMATE2024
    ) %>%
  arrange(Rank) %>%
  
    knitr::kable(
      caption = "Population Data for Iowa",
       col.names = c("Place Name", "Rank", "2020 Pop. Est.", "2021 Pop. Est.", "2022 Pop. Est.", "2023 Pop. Est.", "2024 Pop. Est."),
      format.args = list(big.mark = ",")
    )

 ranked_cities_by_iowa
Population Data for Iowa
Place Name Rank 2020 Pop. Est. 2021 Pop. Est. 2022 Pop. Est. 2023 Pop. Est. 2024 Pop. Est.
Des Moines city 1 214,058 212,539 210,950 211,088 213,096
Cedar Rapids city 2 137,732 137,112 136,861 136,637 137,904
Davenport city 3 101,730 101,052 100,469 100,509 100,938
Sioux City city 4 85,894 85,904 85,568 86,284 86,875
Ankeny city 5 68,095 70,635 72,416 74,822 76,727
Iowa City city 6 74,825 75,180 75,867 76,111 76,710
West Des Moines city 7 68,725 69,913 70,751 72,518 73,664
Ames city 8 66,432 67,028 67,329 68,428 69,026
Waterloo city 9 67,309 66,895 66,530 66,856 67,477
Council Bluffs city 10 62,781 62,582 62,405 62,535 62,665
Dubuque city 11 59,676 59,281 58,928 58,941 58,987
Urbandale city 12 45,577 45,998 46,650 46,931 47,759
Marion city 13 41,552 41,660 41,607 42,127 42,542
Cedar Falls city 14 40,728 40,672 40,584 41,095 41,417
Bettendorf city 15 39,101 39,333 39,584 39,941 40,281
Waukee city 16 23,949 26,487 29,154 31,750 34,420
Marshalltown city 17 27,592 27,461 27,491 27,737 27,886
Mason City city 18 27,351 27,180 26,909 26,954 26,948
Ottumwa city 19 25,545 25,408 25,217 25,438 25,648
Johnston city 20 23,937 24,092 24,225 24,598 25,022
Fort Dodge city 21 24,871 25,036 24,705 24,702 24,886
Clinton city 22 24,467 24,463 24,356 24,230 24,118
Coralville city 23 22,333 22,939 23,191 23,723 23,959
Burlington city 24 24,022 23,769 23,606 23,659 23,637
Muscatine city 25 23,809 23,496 23,499 23,400 23,298

Waukeeeeeeee!! 😈

Looking for the outliers

While I don’t think I understand my data well enough!

Code
local_data %>%
    filter(SUMLEV %in% c("162", "61", "50")) %>%
    group_by(STNAME) %>%
    summarise(Count = n(), .groups = 'drop') %>% 
    arrange(Count) %>%
    knitr::kable(
      caption = "Count of Selected Places per State", 
      col.names = c("State Name", "Selected Places")
    )
Count of Selected Places per State
State Name Selected Places
District of Columbia 1
Hawaii 1
Rhode Island 8
New Hampshire 13
Nevada 19
Maine 23
Connecticut 30
Vermont 39
Delaware 57
Massachusetts 58
Arizona 91
Wyoming 99
New Mexico 105
Montana 127
Alaska 149
Maryland 157
Idaho 198
Virginia 227
West Virginia 230
Oregon 240
Utah 255
Colorado 271
South Carolina 271
Washington 281
Mississippi 299
Louisiana 304
South Dakota 310
New Jersey 323
Tennessee 345
North Dakota 355
Florida 411
Kentucky 418
Alabama 463
California 482
Arkansas 500
Nebraska 528
Michigan 533
Georgia 537
North Carolina 549
Indiana 566
Oklahoma 591
New York 595
Wisconsin 607
Kansas 626
Minnesota 855
Ohio 923
Missouri 938
Iowa 940
Pennsylvania 1013
Texas 1224
Illinois 1294

Check out Hawaii, New Hampshire, and Rhode Island below. My filtering above is not capturing what I believe reality is, or rather what typical non-Census Bureau people think about what should be on that list. I suspect I am filtering a variable called SUMLEV incorrectly or incorrectly for some states, which might have special ways they show up in the data.