By Aaron J. Becker
October 15, 2025
If you’ve seen any data visualizations of gender-neutral names, you’ve probably seen one that looks like this:
This format goes back to Randal Olson’s post on gender-neutral names from 2014, and it’s a great way to visualize which names are the most gender-neutral in the aggregate over a given time period.
In the past 10 years society has, for the most part1, become more comfortable with gender ambiguity and the notion that identity doesn’t have to be binary. I’m not an expert here— I’m going to use terms like “gender-neutral” and “non-binary” interchangeably, maybe even throwing a “unisex” or “androgynous” in there occasionally. Whatever label you want to use, ambiguity has moved past its sketch comedy phase and is now much more mainstream, with some parents even opting for unisex names to avoid gender-based employment discrimination in the future.
So let’s use the latest vintage of Social Security Administration baby name data to see how the population of gender-neutral names has changed over time.
This post was published directly from the Python notebook used to conduct the data analysis— feel free to skip over the code blocks if that’s not your thing, or use the following button to hide all the code blocks:
contents
# this code block imports and configures the tools that we'll be using throughout the notebook
from pathlib import Path
import sys, os
import polars as pl
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
# table formatting helpers
from great_tables import style, loc, md
# matplotlib theming
from aquarel import load_theme
theme = load_theme("gruvbox_light")
theme.apply()
# customize font in visualizations
mpl.rcParams['font.family'] = 'monospace'
mpl.rcParams['font.size'] = 14
# always show all rows in polars dataframes
pl.Config.set_tbl_rows(-1)
# show all list items when displaying list columns in polars
pl.Config.set_fmt_table_cell_list_len(-1)
# configure python import path for helper modules
sys.path.append(Path(os.getcwd()).parent.parent.parent.as_posix())
# import the helper module
from fetch_ssa_data.fetch_ssa_data import get_cleaned_national_df
placeholder names
Some of the most “gender-neutral” names in the SSA dataset are actually clerical placeholders, like “Unknown” or “Infant”. Hospitals have been applying for Social Security numbers on behalf of nearly all newborns in the United States since 1985, when the SSA introduced the “Enumeration at Birth” program. If parents haven’t decided on a name by the time they leave the hospital, placeholder names remain on the applications and work their way into the SSA’s baby name data.
Since these names are almost always legally changed, we’ll remove them from the dataset.
# remove the name "Unknown" and other clerical placeholders
clerical_placeholders = ['Unknown', 'Infant', 'Baby']
name_data = get_cleaned_national_df().filter(~pl.col('name').is_in(clerical_placeholders))
data filtering
I’m going to start by keeping the 5,000 births threshold that Randal Olson used in his post, but I’ll show several additional versions with different thresholds below.
Olson started his data in 1986, which is probably personally relevant to him. But since the average Reddit user is probably much younger, I’ll focus on births since 2000 for the initial analysis, structuring the code to allow for different starting years and birth thresholds.
def filter_name_data(name_data, start_year, total_births_threshold, end_year = None):
if end_year is None:
end_year = name_data.select(pl.col('year').max()).item()
n_before = name_data.select(pl.struct('name', 'gender')).n_unique()
# print(f"before filtering: {n_before} unique name + gender combinations")
filtered_data = name_data.filter(pl.col('year') >= start_year, pl.col('year') <= end_year)\
.filter(
# we have to apply birth count threshold after filtering min year to count only births after that year
pl.col('births').sum().over('name') >= total_births_threshold,
# only include names with at least some gender diversity (both sums > 0)
pl.col('births').filter(pl.col('gender') == 'M').sum().over('name') > 0,
pl.col('births').filter(pl.col('gender') == 'F').sum().over('name') > 0
)
n_after = filtered_data.select(pl.struct('name', 'gender')).n_unique()
# print(f"After filtering: {n_after} unique name + gender combinations")
# for this analysis, we only need aggregated data at the name level (no time series)
filtered_data = filtered_data.group_by('name').agg(
# sum is only necessary for gendered birth counts to prevent result from being a list
pl.col('births').filter(pl.col('gender') == 'M').sum().alias('male_births'),
pl.col('births').filter(pl.col('gender') == 'F').sum().alias('female_births'),
pl.col('births').sum().alias('total_births'),
# for normalizing period comparisons later on
pl.col('year').n_unique().alias('n_years')
).with_columns(
(pl.col('male_births') / pl.col('total_births')).alias('pct_male'),
(pl.col('female_births') / pl.col('total_births')).alias('pct_female'),
).sort('name')
return filtered_data
filtered_data = filter_name_data(name_data, 2000, 5000)
measuring gender-neutrality
I’m going to use a diversity index, known as the Simpson index in ecology or Herfindahl–Hirschman index in economics, to quantify gender-neutrality2. Whatever you call it, you calculate it by squaring the fraction belonging to each category (in this case sex) and summing the squares, subtracting the result from 1. I’m not going to write a formula in LaTeX here, but here’s the code:
def calculate_gender_neutrality(filtered_data):
return filtered_data.with_columns(
(1 - (pl.col('pct_male') ** 2 + pl.col('pct_female') ** 2)).alias('diversity_index')
)
filtered_data = calculate_gender_neutrality(filtered_data)
visualizing gender-neutrality
This visualization format borrows directly from Randal Olson’s original: horizontal bars with gender proportions plotted as percentages, a vertical line at 50% gender parity, and birth counts plotted at the ends of the bars. Here’s the code to create this plot in Python:
def plot_gender_neutral_names(df: pl.DataFrame,
n_plots: int = 25,
start_year: int = 2000,
end_year: int = 2024,
total_births_threshold: int = 5000,
figsize: tuple = (10, 12),
title=None) -> plt.Figure:
"""
Create a horizontal stacked bar chart showing gender distribution for names.
This code was generated with partial assistance from Claude.
Parameters:
-----------
df : pl.DataFrame
DataFrame with columns: name, pct_female, pct_male, female_births, male_births
Data must already be sorted and filtered.
title : str
Chart title
figsize : tuple
Figure size (width, height)
Returns:
--------
fig : matplotlib.figure.Figure
"""
barheight = 0.8 # used to set axis limits, declared here for consistency
male_color = '#1c398e'
female_color = '#82181a'
# Extract data
names = df['name'].to_list()
pct_female = df['pct_female'].to_list()
pct_male = df['pct_male'].to_list()
female_births = df['female_births'].to_list()
male_births = df['male_births'].to_list()
if title is None:
title = f"the {n_plots} most androgynous baby names in the U.S.,\n{start_year}-{end_year} (sorted by diversity)"
# Create figure
fig, ax = plt.subplots(figsize=figsize)
# Y positions for bars
y_pos = range(len(names))
# Create stacked horizontal bars
# Female portion (left side, from 0 to pct_female)
ax.barh(y_pos, pct_female, height=barheight, color=female_color, label='Female', alpha=0.9)
# Male portion (right side, from pct_female to 1.0)
ax.barh(y_pos, pct_male, left=pct_female, height=barheight, color=male_color, label='Male', alpha=0.9)
# Add vertical line at 0.5 (gender parity) - limited to plot area
ax.axvline(x=0.5, color='white', linewidth=2, zorder=3, ymin=0, ymax=1)
# Add text labels for birth counts
girls_labeled = False
boys_labeled = False
for i, (name, pf, pm, fb, mb) in enumerate(zip(names, pct_female, pct_male, female_births, male_births)):
# only show "girls" or "boys" once
# Female births label (left-aligned, near left edge of bar)
if pf > 0.05: # Only show if there's enough space
if i == 0 or not girls_labeled:
ax.text(0.02, i, f'{fb:,} girls', ha='left', va='center',
color='white', fontsize=12, fontweight='bold')
girls_labeled = True
else:
ax.text(0.02, i, f'{fb:,}', ha='left', va='center',
color='white', fontsize=12, fontweight='bold')
# Male births label (right-aligned, near right edge of bar)
if pm > 0.05: # Only show if there's enough space
if i == 0 or not boys_labeled:
ax.text(0.98, i, f'{mb:,} boys', ha='right', va='center',
color='white', fontsize=12, fontweight='bold')
boys_labeled = True
else:
ax.text(0.98, i, f'{mb:,}', ha='right', va='center',
color='white', fontsize=12, fontweight='bold')
# Set y-axis labels (names)
ax.set_ylim(-barheight/2, len(names) - barheight/2)
ax.set_yticks(y_pos)
ax.set_yticklabels(names, fontsize=18, fontweight='bold')
ax.invert_yaxis() # Top to bottom
# Set x-axis limits and hide
ax.set_xlim(0, 1)
ax.set_xticks([])
# Add title with reduced padding
ax.set_title(title, fontsize=22, fontweight='bold')
# Remove spines
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
# Add note at bottom
note_text = f"Data source: Social Security Administration\nIncludes names that were given to at least {total_births_threshold:,} babies between {start_year} and {end_year}."
fig.text(0.01, -0.02, note_text, ha='left', fontsize=11)
# add nameplay.org link
fig.text(0.98, -0.02, "NamePlay.org", ha='right', fontsize=20, fontweight='bold')
plt.tight_layout()
plt.show()
return fig
top 25 gender-neutral names: 2000-2024
Without further ado, here are the top 25 gender-neutral names in the U.S. from 2000 to 2024, ranked by greatest gender-neutrality:
n_plots = 25
plot_data = filtered_data.sort('diversity_index', descending=True).head(n_plots)
fig = plot_gender_neutral_names(plot_data)
spelling variations
Some of these names are clearly spelling variations of more popular names! We even get 2 different versions of “Riley” in the top 25. Here’s a table with some stats on the most obvious spelling variations that I spotted in the chart above:
plotted_names = plot_data.select('name').to_series().to_list()
pl.DataFrame({'name': [
'Austin', 'Austyn',
'Camden', 'Camdyn',
'Riley', 'Ryley', 'Reilly',
'Ryan', 'Rian',
'Skyler', 'Skylar'
]})\
.join(filtered_data, on='name', how='left').drop('n_years')\
.style\
.tab_style(style=style.fill(color=pl.when(pl.col('name').is_in(plotted_names)).then(pl.lit('#e9d4ff')).otherwise(None)), locations=loc.body('name'))\
.fmt(lambda n: f'<a href="/name/{n}" target="_blank" class="textLink">{n}</a>', ["name"])\
.fmt_integer(['male_births', 'female_births', 'total_births'])\
.fmt_number(['diversity_index'], decimals=2)\
.fmt_percent(['pct_male', 'pct_female'], decimals=1)\
.tab_source_note('Source: Social Security Administration, NamePlay.org')\
.tab_header('selected gender-neutral names and spelling variants',
subtitle='names from plot above are highlighted')\
.cols_label({
'male_births': 'boys',
'female_births': 'girls',
'total_births': 'total births',
'diversity_index': 'diversity index',
'pct_male': '% male',
'pct_female': '% female',
})\
.show()
| selected gender-neutral names and spelling variants |
| names from plot above are highlighted |
nameboysgirlstotal births% male% femalediversity index
| Austin | 189,824 | 3,070 | 192,894 | 98.4% | 1.6% | 0.03 |
| Austyn | 3,383 | 4,228 | 7,611 | 44.4% | 55.6% | 0.49 |
| Camden | 54,238 | 3,283 | 57,521 | 94.3% | 5.7% | 0.11 |
| Camdyn | 3,784 | 2,767 | 6,551 | 57.8% | 42.2% | 0.49 |
| Riley | 70,375 | 126,415 | 196,790 | 35.8% | 64.2% | 0.46 |
| Ryley | 2,800 | 2,805 | 5,605 | 50.0% | 50.0% | 0.50 |
| Reilly | 3,241 | 3,757 | 6,998 | 46.3% | 53.7% | 0.50 |
| Ryan | 286,625 | 14,830 | 301,455 | 95.1% | 4.9% | 0.09 |
| Rian | 3,283 | 2,353 | 5,636 | 58.3% | 41.7% | 0.49 |
| Skyler | 25,360 | 21,352 | 46,712 | 54.3% | 45.7% | 0.50 |
| Skylar | 10,646 | 75,916 | 86,562 | 12.3% | 87.7% | 0.22 |
| Source: Social Security Administration, NamePlay.org |
Camdyn and Austyn are feminine variants of Camden and Austin, respectively, which both skew more male. Ryan remains staunchly masculine, but Rian is split almost evenly. Skylar is both more popular and more feminine than its gender-neutral counterpart, Skyler. Ryley and Reilly are both more evenly balanced than Riley, which is now almost two thirds female.
You can explore more names that are gender-neutral when spellings are grouped in this interactive listing.
top 25 gender-neutral names: 1980-2024
Since we have all the code set up, let’s see what this ranking chart looks like when we expand the time period to 1980-2024:
start_year = 1980
births_threshold = 5000
filtered_data_1980 = filter_name_data(name_data, start_year, births_threshold)
filtered_data_1980 = calculate_gender_neutrality(filtered_data_1980)
plot_data_1980 = filtered_data_1980.sort('diversity_index', descending=True).head(25)
fig = plot_gender_neutral_names(plot_data_1980, start_year=start_year, total_births_threshold=births_threshold)
where were they? 2000-2024 vs. 1980-1999
There’s lots of overlap, which is to be expected, because more than half of the data in a 1980-2024 dataset is from 2000-2024. We can prepare data covering only 1980-1999 to see what our more recent list of unisex names looked like back then:
data_1980_1999 = calculate_gender_neutrality(filter_name_data(name_data, 1980, 0, 1999))
comparison_data_1980_2000 = plot_data.select('name', (pl.col('total_births') / pl.col('n_years')).alias('births_per_year'), 'pct_male').join(
data_1980_1999.select('name', (pl.col('total_births') / pl.col('n_years')).alias('births_per_year'), 'pct_male'), on='name', how='left', suffix='_1980_1999', maintain_order='left').select('name',
pl.col('births_per_year_1980_1999'), pl.col('births_per_year').alias('births_per_year_2000_2024'),
((pl.col('births_per_year') - pl.col('births_per_year_1980_1999')) / pl.col('births_per_year_1980_1999')).alias('births_per_year_growth'),
pl.col('pct_male_1980_1999'), pl.col('pct_male').alias('pct_male_2000_2024'), (pl.col('pct_male') - pl.col('pct_male_1980_1999')).alias('pct_male_change'))
comparison_data_1980_2000.style\
.fmt(lambda n: f'<a href="/name/{n}" target="_blank" class="textLink">{n}</a>', ["name"])\
.tab_source_note('Source: Social Security Administration, NamePlay.org')\
.tab_source_note('Births per year are normalized for the number of years in the dataset.')\
.tab_header('where were they? most unisex names from 2000-2024 in 1980-1999',
subtitle='names appearing in both 2000-2024 and 1980-2024 charts are highlighted')\
.fmt_number(['births_per_year_2000_2024', 'births_per_year_1980_1999'], decimals=1)\
.cols_label({
'births_per_year_2000_2024': md('births per year,<br/>2000-2024'),
'births_per_year_1980_1999': md('births per year,<br/>1980-1999'),
'pct_male_2000_2024': md('% boys,<br/>2000-2024'),
'pct_male_1980_1999': md('% boys,<br/>1980-1999'),
'pct_male_change': '% boys change',
'births_per_year_growth': md('births per year<br/>growth'),
})\
.tab_style(style=style.fill(color=pl.when(pl.col('name').is_in(plot_data_1980['name'])).then(pl.lit('#e9d4ff')).otherwise(None)), locations=loc.body('name'))\
.data_color(columns=['births_per_year_growth'], palette=["#ff6467","#fee685","#00c950"], domain=[-1, 1], truncate=True)\
.data_color(columns=['pct_male_change'], palette=["#fb2c36","#fee685","#2b7fff"], domain=[-0.5, 0.5], truncate=True)\
.data_color(columns=['pct_male_2000_2024', 'pct_male_1980_1999'], palette=["#fb2c36","#2b7fff"], domain=[0, 1], truncate=True)\
.fmt_percent(['births_per_year_growth', 'pct_male_2000_2024', 'pct_male_1980_1999', 'pct_male_change'], decimals=1)\
.show()
| where were they? most unisex names from 2000-2024 in 1980-1999 |
| names appearing in both 2000-2024 and 1980-2024 charts are highlighted |
namebirths per year,
1980-1999births per year,
2000-2024births per year
growth% boys,
1980-1999% boys,
2000-2024% boys change
| Ryley | 80.2 | 224.2 | 179.7% | 67.4% | 50.0% | −17.4% |
| Frankie | 441.1 | 486.2 | 10.2% | 76.3% | 50.1% | −26.2% |
| Justice | 495.3 | 1,105.9 | 123.3% | 51.6% | 50.2% | −1.3% |
| Jaylin | 181.3 | 673.4 | 271.4% | 66.9% | 51.0% | −15.9% |
| Murphy | 61.9 | 268.8 | 334.2% | 70.0% | 48.8% | −21.2% |
| Jael | 49.1 | 258.3 | 426.1% | 19.7% | 47.1% | 27.4% |
| Briar | 53.3 | 443.0 | 731.0% | 71.7% | 46.9% | −24.8% |
| Jackie | 708.4 | 207.6 | −70.7% | 37.2% | 46.7% | 9.5% |
| Landry | 43.0 | 397.4 | 825.3% | 70.7% | 46.6% | −24.1% |
| Reilly | 147.1 | 279.9 | 90.3% | 42.1% | 46.3% | 4.2% |
| Skyler | 1,084.4 | 1,868.5 | 72.3% | 72.9% | 54.3% | −18.6% |
| Azariah | 28.0 | 494.7 | 1,666.9% | 56.9% | 45.6% | −11.3% |
| Marion | 295.4 | 223.0 | −24.5% | 47.9% | 45.3% | −2.7% |
| Ocean | 50.3 | 274.9 | 446.1% | 38.9% | 55.1% | 16.2% |
| Charlie | 620.2 | 2,536.2 | 308.9% | 83.7% | 55.2% | −28.4% |
| Austyn | 126.3 | 304.4 | 141.1% | 76.2% | 44.4% | −31.8% |
| Armani | 197.0 | 941.4 | 377.8% | 59.1% | 56.0% | −3.1% |
| Bellamy | None | 273.9 | None | None | 43.7% | None |
| Jessie | 2,098.4 | 844.7 | −59.7% | 49.7% | 43.2% | −6.5% |
| Finley | 14.3 | 1,539.0 | 10,637.5% | 71.2% | 42.6% | −28.6% |
| Dominique | 2,741.3 | 592.7 | −78.4% | 29.3% | 42.4% | 13.1% |
| Camdyn | 17.0 | 262.0 | 1,441.4% | 47.1% | 57.8% | 10.7% |
| Hollis | 73.9 | 201.7 | 173.0% | 61.9% | 57.8% | −4.1% |
| Rian | 117.4 | 225.4 | 92.0% | 60.2% | 58.3% | −1.9% |
| Dakota | 2,941.3 | 2,928.0 | −0.5% | 83.5% | 58.4% | −25.1% |
| Source: Social Security Administration, NamePlay.org |
| Births per year are normalized for the number of years in the dataset. |
Only one name from the 2000-2024 list has no data from 1980-2000: “Bellamy”, which is a transferred usage of the surname that only appeared in the data since 2000. Only a handful of the most recently most unisex names declined in popularity compared to 1980-2000: Dominique, Jackie, Jessie, and Marion, all among the most “classic” gender-neutral names. Parents aren’t letting their desire for a unisex name limit them to traditionally-unisex choices.
This data also reinforces the observation that names tend to become more gender-neutral when historically masculine names are adopted by parents of baby girls: more than 70% of the unisex names from 2000-2024 were more feminine than they were in 1980-2000. Here are the names that became more feminine:
more_feminine_1980_2000 = comparison_data_1980_2000.filter(pl.col('pct_male_change') < 0).sort('pct_male_change')
more_feminine_1980_2000.style\
.fmt_number(['births_per_year_2000_2024', 'births_per_year_1980_1999'], decimals=1)\
.cols_label({
'births_per_year_2000_2024': md('births per year,<br/>2000-2024'),
'births_per_year_1980_1999': md('births per year,<br/>1980-1999'),
'pct_male_2000_2024': md('% boys,<br/>2000-2024'),
'pct_male_1980_1999': md('% boys,<br/>1980-1999'),
'pct_male_change': '% boys change',
'births_per_year_growth': md('births per year<br/>growth'),
})\
.tab_style(style=style.fill(color=pl.when(pl.col('name').is_in(plot_data_1980['name'])).then(pl.lit('#e9d4ff')).otherwise(None)), locations=loc.body('name'))\
.data_color(columns=['births_per_year_growth'], palette=["#ff6467","#fee685","#00c950"], domain=[-1, 1], truncate=True)\
.data_color(columns=['pct_male_change'], palette=["#fb2c36","#fee685","#2b7fff"], domain=[-0.5, 0.5], truncate=True)\
.data_color(columns=['pct_male_2000_2024', 'pct_male_1980_1999'], palette=["#fb2c36","#2b7fff"], domain=[0, 1], truncate=True)\
.fmt_percent(['births_per_year_growth', 'pct_male_2000_2024', 'pct_male_1980_1999', 'pct_male_change'], decimals=1)\
.tab_header(title='top unisex names from 2000-2024 that became more feminine vs. 1980-1999',
subtitle=md(f'{len(more_feminine_1980_2000)} out of {len(comparison_data_1980_2000)} names became more feminine.<br/>names appearing in both 2000-2024 and 1980-1999 charts are highlighted purple.'))\
.show()
| top unisex names from 2000-2024 that became more feminine vs. 1980-1999 |
18 out of 25 names became more feminine. names appearing in both 2000-2024 and 1980-1999 charts are highlighted purple. |
namebirths per year,
1980-1999births per year,
2000-2024births per year
growth% boys,
1980-1999% boys,
2000-2024% boys change
| Austyn | 126.3 | 304.4 | 141.1% | 76.2% | 44.4% | −31.8% |
| Finley | 14.3 | 1,539.0 | 10,637.5% | 71.2% | 42.6% | −28.6% |
| Charlie | 620.2 | 2,536.2 | 308.9% | 83.7% | 55.2% | −28.4% |
| Frankie | 441.1 | 486.2 | 10.2% | 76.3% | 50.1% | −26.2% |
| Dakota | 2,941.3 | 2,928.0 | −0.5% | 83.5% | 58.4% | −25.1% |
| Briar | 53.3 | 443.0 | 731.0% | 71.7% | 46.9% | −24.8% |
| Landry | 43.0 | 397.4 | 825.3% | 70.7% | 46.6% | −24.1% |
| Murphy | 61.9 | 268.8 | 334.2% | 70.0% | 48.8% | −21.2% |
| Skyler | 1,084.4 | 1,868.5 | 72.3% | 72.9% | 54.3% | −18.6% |
| Ryley | 80.2 | 224.2 | 179.7% | 67.4% | 50.0% | −17.4% |
| Jaylin | 181.3 | 673.4 | 271.4% | 66.9% | 51.0% | −15.9% |
| Azariah | 28.0 | 494.7 | 1,666.9% | 56.9% | 45.6% | −11.3% |
| Jessie | 2,098.4 | 844.7 | −59.7% | 49.7% | 43.2% | −6.5% |
| Hollis | 73.9 | 201.7 | 173.0% | 61.9% | 57.8% | −4.1% |
| Armani | 197.0 | 941.4 | 377.8% | 59.1% | 56.0% | −3.1% |
| Marion | 295.4 | 223.0 | −24.5% | 47.9% | 45.3% | −2.7% |
| Rian | 117.4 | 225.4 | 92.0% | 60.2% | 58.3% | −1.9% |
| Justice | 495.3 | 1,105.9 | 123.3% | 51.6% | 50.2% | −1.3% |
The data also shows that most names which became more feminine also grew in popularity. The only decliners in the table above are, again, those “classic” androgynous names.
more_masc_1980_2000 = comparison_data_1980_2000.filter(pl.col('pct_male_change') > 0).sort('pct_male_change', descending=True)
more_masc_1980_2000.style\
.fmt_number(['births_per_year_2000_2024', 'births_per_year_1980_1999'], decimals=1)\
.cols_label({
'births_per_year_2000_2024': md('births per year,<br/>2000-2024'),
'births_per_year_1980_1999': md('births per year,<br/>1980-1999'),
'pct_male_2000_2024': md('% boys,<br/>2000-2024'),
'pct_male_1980_1999': md('% boys,<br/>1980-1999'),
'pct_male_change': '% boys change',
'births_per_year_growth': md('births per year<br/>growth'),
})\
.tab_style(style=style.fill(color=pl.when(pl.col('name').is_in(plot_data_1980['name'])).then(pl.lit('#e9d4ff')).otherwise(None)), locations=loc.body('name'))\
.data_color(columns=['births_per_year_growth'], palette=["#ff6467","#fee685","#00c950"], domain=[-1, 1], truncate=True)\
.data_color(columns=['pct_male_change'], palette=["#fb2c36","#fee685","#2b7fff"], domain=[-0.5, 0.5], truncate=True)\
.data_color(columns=['pct_male_2000_2024', 'pct_male_1980_1999'], palette=["#fb2c36","#2b7fff"], domain=[0, 1], truncate=True)\
.fmt_percent(['births_per_year_growth', 'pct_male_2000_2024', 'pct_male_1980_1999', 'pct_male_change'], decimals=1)\
.tab_header(title='top unisex names from 2000-2024 that became more masculine vs. 1980-1999',
subtitle=md(f'{len(more_masc_1980_2000)} out of {len(comparison_data_1980_2000)} names became more masculine.<br/>names appearing in both 2000-2024 and 1980-2024 charts are highlighted purple.'))\
.show()
| top unisex names from 2000-2024 that became more masculine vs. 1980-1999 |
6 out of 25 names became more masculine. names appearing in both 2000-2024 and 1980-2024 charts are highlighted purple. |
namebirths per year,
1980-1999births per year,
2000-2024births per year
growth% boys,
1980-1999% boys,
2000-2024% boys change
| Jael | 49.1 | 258.3 | 426.1% | 19.7% | 47.1% | 27.4% |
| Ocean | 50.3 | 274.9 | 446.1% | 38.9% | 55.1% | 16.2% |
| Dominique | 2,741.3 | 592.7 | −78.4% | 29.3% | 42.4% | 13.1% |
| Camdyn | 17.0 | 262.0 | 1,441.4% | 47.1% | 57.8% | 10.7% |
| Jackie | 708.4 | 207.6 | −70.7% | 37.2% | 46.7% | 9.5% |
| Reilly | 147.1 | 279.9 | 90.3% | 42.1% | 46.3% | 4.2% |
It seems like Jackie and Dominique became more masculine mainly because their popularity among parents of girls declined, which is consistent with girls’ names being more prone to fashion trends. The rest of the above names seem to have become more unisex by gaining popularity among parents of boys, which bucks the usual trend.
most unisex names of the 1980s and 1990s
We can also generate this plot for only the 1980s and 1990s, isolating the different trends seen in the two charts above. This will also allow us to see what happened to the most unisex names of the 1980s and 1990s in the years since 2000.
start_year = 1980
end_year = 1999
births_threshold = 5000
filtered_data_1980_1999 = filter_name_data(name_data, start_year, births_threshold, end_year)
filtered_data_1980_1999 = calculate_gender_neutrality(filtered_data_1980_1999)
plot_data_1980_1999 = filtered_data_1980_1999.sort('diversity_index', descending=True).head(25)
fig = plot_gender_neutral_names(plot_data_1980_1999, start_year=start_year, end_year=end_year, total_births_threshold=births_threshold)
1980s and 1990s unisex names in 2000-2024
What happened to the unisex names of the 1980s and 1990s in the years since 2000? Let’s find out:
# in this comparison 1980_1999 is the left dataframe joined to 2000_2024 on the right (which receives suffix _2000_2024)
comparison_data_1980_1999_2000_2024 = plot_data_1980_1999.select('name', (pl.col('total_births') / pl.col('n_years')).alias('births_per_year'), 'pct_male').join(
calculate_gender_neutrality(filter_name_data(name_data, 2000, 0, 2024)).select('name', (pl.col('total_births') / pl.col('n_years')).alias('births_per_year'), 'pct_male'), on='name', how='left', suffix='_2000_2024', maintain_order='left').select('name',
pl.col('births_per_year').alias('births_per_year_1980_1999'), pl.col('births_per_year_2000_2024'),
((pl.col('births_per_year_2000_2024') - pl.col('births_per_year')) / pl.col('births_per_year')).alias('births_per_year_growth'),
pl.col('pct_male').alias('pct_male_1980_1999'), pl.col('pct_male_2000_2024'), (pl.col('pct_male_2000_2024') - pl.col('pct_male')).alias('pct_male_change'))
comparison_data_1980_1999_2000_2024.style\
.fmt(lambda n: f'<a href="/name/{n}" target="_blank" class="textLink">{n}</a>', ["name"])\
.tab_source_note('Source: Social Security Administration, NamePlay.org')\
.tab_source_note('Births per year are normalized for the number of years in the dataset.')\
.tab_header(md('where are they now?<br/>most unisex names from 1980-1999 in 2000-2024'),
subtitle='names appearing in both 1980-1999 and 2000-2024 charts are highlighted')\
.fmt_number(['births_per_year_2000_2024', 'births_per_year_1980_1999'], decimals=1)\
.cols_label({
'births_per_year_2000_2024': md('births per year,<br/>2000-2024'),
'births_per_year_1980_1999': md('births per year,<br/>1980-1999'),
'pct_male_2000_2024': md('% boys,<br/>2000-2024'),
'pct_male_1980_1999': md('% boys,<br/>1980-1999'),
'pct_male_change': '% boys change',
'births_per_year_growth': md('births per year<br/>growth'),
})\
.tab_style(style=style.fill(color=pl.when(pl.col('name').is_in(plot_data['name'])).then(pl.lit('#e9d4ff')).otherwise(None)), locations=loc.body('name'))\
.data_color(columns=['births_per_year_growth'], palette=["#ff6467","#fee685","#00c950"], domain=[-1, 1], truncate=True)\
.data_color(columns=['pct_male_change'], palette=["#fb2c36","#fee685","#2b7fff"], domain=[-0.5, 0.5], truncate=True)\
.data_color(columns=['pct_male_2000_2024', 'pct_male_1980_1999'], palette=["#fb2c36","#2b7fff"], domain=[0, 1], truncate=True)\
.fmt_percent(['births_per_year_growth', 'pct_male_2000_2024', 'pct_male_1980_1999', 'pct_male_change'], decimals=1)\
.show()
where are they now? most unisex names from 1980-1999 in 2000-2024 |
| names appearing in both 1980-1999 and 2000-2024 charts are highlighted |
namebirths per year,
1980-1999births per year,
2000-2024births per year
growth% boys,
1980-1999% boys,
2000-2024% boys change
| Jessie | 2,098.4 | 844.7 | −59.7% | 49.7% | 43.2% | −6.5% |
| Justice | 495.3 | 1,105.9 | 123.3% | 51.6% | 50.2% | −1.3% |
| Kirby | 266.1 | 86.9 | −67.3% | 51.6% | 59.9% | 8.3% |
| Sidney | 745.3 | 699.6 | −6.1% | 48.0% | 31.3% | −16.7% |
| Marion | 295.4 | 223.0 | −24.5% | 47.9% | 45.3% | −2.7% |
| Loren | 591.2 | 224.5 | −62.0% | 47.8% | 35.3% | −12.5% |
| Sage | 323.5 | 1,458.6 | 350.9% | 47.1% | 32.0% | −15.1% |
| Carey | 265.6 | 41.0 | −84.6% | 46.9% | 63.2% | 16.3% |
| Peyton | 727.5 | 4,886.4 | 571.7% | 46.7% | 35.7% | −11.1% |
| Jaime | 2,425.4 | 968.7 | −60.1% | 54.5% | 86.7% | 32.3% |
| Jean | 614.6 | 282.2 | −54.1% | 45.4% | 72.9% | 27.5% |
| Casey | 5,951.7 | 1,447.6 | −75.7% | 54.8% | 61.4% | 6.6% |
| Harley | 718.9 | 1,356.5 | 88.7% | 54.9% | 29.9% | −25.0% |
| Ashton | 1,120.5 | 3,284.2 | 193.1% | 45.1% | 91.2% | 46.0% |
| Addison | 411.2 | 5,914.7 | 1,338.2% | 55.2% | 3.8% | −51.3% |
| Shea | 413.3 | 390.8 | −5.4% | 44.6% | 37.0% | −7.6% |
| Devyn | 261.6 | 427.6 | 63.5% | 43.9% | 41.6% | −2.3% |
| Jody | 555.1 | 98.4 | −82.3% | 43.4% | 68.5% | 25.1% |
| Skylar | 848.0 | 3,462.5 | 308.3% | 43.3% | 12.3% | −31.0% |
| Kendall | 1,473.2 | 2,217.1 | 50.5% | 42.7% | 16.3% | −26.4% |
| Darian | 520.6 | 407.2 | −21.8% | 57.4% | 85.1% | 27.8% |
| Payton | 635.1 | 2,804.2 | 341.5% | 42.3% | 25.9% | −16.4% |
| Angel | 4,451.1 | 8,868.2 | 99.2% | 59.3% | 81.9% | 22.6% |
| Kristian | 578.6 | 483.4 | −16.4% | 59.7% | 88.0% | 28.3% |
| Kerry | 897.4 | 129.7 | −85.5% | 38.5% | 60.1% | 21.6% |
| Source: Social Security Administration, NamePlay.org |
| Births per year are normalized for the number of years in the dataset. |
Well, for starters, they drifted away from the gender parity. Only 3 out of 25 remained among the most evenly-split names in the decades that followed. Compared to the previous tables, which looked at how the most unisex names from 2000-2024 changed relative to the 1980-1999 data, the gender shifts are more diverse this time around.
This is a bit tautological, of course, because in the previous comparison we were looking at names we knew to be the most unisex in the 2000-2024 data; of course the most unisex names in a period were more likely to have changed to become more unisex leading up to that period… otherwise they wouldn’t be the most unisex names.
But recall that how our most even 2000-2024 names came to be be unisex was interesting— when names became more unisex, they typically did so by becoming more feminine. But looking at the 1980-1999 androgynous names moving into the 2000-2024 data, the results are more mixed:
more_feminine_1980_1999_2000_2024 = comparison_data_1980_1999_2000_2024.filter(pl.col('pct_male_change') < 0).sort('pct_male_change')
more_feminine_1980_1999_2000_2024.style\
.fmt_number(['births_per_year_2000_2024', 'births_per_year_1980_1999'], decimals=1)\
.cols_label({
'births_per_year_2000_2024': md('births per year,<br/>2000-2024'),
'births_per_year_1980_1999': md('births per year,<br/>1980-1999'),
'pct_male_2000_2024': md('% boys,<br/>2000-2024'),
'pct_male_1980_1999': md('% boys,<br/>1980-1999'),
'pct_male_change': '% boys change',
'births_per_year_growth': md('births per year<br/>growth'),
})\
.tab_style(style=style.fill(color=pl.when(pl.col('name').is_in(plot_data['name'])).then(pl.lit('#e9d4ff')).otherwise(None)), locations=loc.body('name'))\
.data_color(columns=['births_per_year_growth'], palette=["#ff6467","#fee685","#00c950"], domain=[-1, 1], truncate=True)\
.data_color(columns=['pct_male_change'], palette=["#fb2c36","#fee685","#2b7fff"], domain=[-0.5, 0.5], truncate=True)\
.data_color(columns=['pct_male_2000_2024', 'pct_male_1980_1999'], palette=["#fb2c36","#2b7fff"], domain=[0, 1], truncate=True)\
.fmt_percent(['births_per_year_growth', 'pct_male_2000_2024', 'pct_male_1980_1999', 'pct_male_change'], decimals=1)\
.tab_header(title='top unisex names from 1980-1999 that became more feminine in 2000-2024',
subtitle=md(f'{len(more_feminine_1980_1999_2000_2024)} out of {len(comparison_data_1980_1999_2000_2024)} names became more feminine.<br/>names appearing in both 2000-2024 and 1980-1999 charts are highlighted purple.'))\
.show()
| top unisex names from 1980-1999 that became more feminine in 2000-2024 |
14 out of 25 names became more feminine. names appearing in both 2000-2024 and 1980-1999 charts are highlighted purple. |
namebirths per year,
1980-1999births per year,
2000-2024births per year
growth% boys,
1980-1999% boys,
2000-2024% boys change
| Addison | 411.2 | 5,914.7 | 1,338.2% | 55.2% | 3.8% | −51.3% |
| Skylar | 848.0 | 3,462.5 | 308.3% | 43.3% | 12.3% | −31.0% |
| Kendall | 1,473.2 | 2,217.1 | 50.5% | 42.7% | 16.3% | −26.4% |
| Harley | 718.9 | 1,356.5 | 88.7% | 54.9% | 29.9% | −25.0% |
| Sidney | 745.3 | 699.6 | −6.1% | 48.0% | 31.3% | −16.7% |
| Payton | 635.1 | 2,804.2 | 341.5% | 42.3% | 25.9% | −16.4% |
| Sage | 323.5 | 1,458.6 | 350.9% | 47.1% | 32.0% | −15.1% |
| Loren | 591.2 | 224.5 | −62.0% | 47.8% | 35.3% | −12.5% |
| Peyton | 727.5 | 4,886.4 | 571.7% | 46.7% | 35.7% | −11.1% |
| Shea | 413.3 | 390.8 | −5.4% | 44.6% | 37.0% | −7.6% |
| Jessie | 2,098.4 | 844.7 | −59.7% | 49.7% | 43.2% | −6.5% |
| Marion | 295.4 | 223.0 | −24.5% | 47.9% | 45.3% | −2.7% |
| Devyn | 261.6 | 427.6 | 63.5% | 43.9% | 41.6% | −2.3% |
| Justice | 495.3 | 1,105.9 | 123.3% | 51.6% | 50.2% | −1.3% |
There’s still a skew towards names becoming more feminine once they pass through being gender neutral. Some names barely budged, like Justice, and most of the names that did become more feminine also grew in popularity. This is consistent with the parents of girls “taking over” formerly gender neutral names. But 11 out of the 25 names did become more masculine:
more_masculine_1980_1999_2000_2024 = comparison_data_1980_1999_2000_2024.filter(pl.col('pct_male_change') > 0).sort('pct_male_change', descending=True)
more_masculine_1980_1999_2000_2024.style\
.fmt_number(['births_per_year_2000_2024', 'births_per_year_1980_1999'], decimals=1)\
.cols_label({
'births_per_year_2000_2024': md('births per year,<br/>2000-2024'),
'births_per_year_1980_1999': md('births per year,<br/>1980-1999'),
'pct_male_2000_2024': md('% boys,<br/>2000-2024'),
'pct_male_1980_1999': md('% boys,<br/>1980-1999'),
'pct_male_change': '% boys change',
'births_per_year_growth': md('births per year<br/>growth'),
})\
.tab_style(style=style.fill(color=pl.when(pl.col('name').is_in(plot_data['name'])).then(pl.lit('#e9d4ff')).otherwise(None)), locations=loc.body('name'))\
.data_color(columns=['births_per_year_growth'], palette=["#ff6467","#fee685","#00c950"], domain=[-1, 1], truncate=True)\
.data_color(columns=['pct_male_change'], palette=["#fb2c36","#fee685","#2b7fff"], domain=[-0.5, 0.5], truncate=True)\
.data_color(columns=['pct_male_2000_2024', 'pct_male_1980_1999'], palette=["#fb2c36","#2b7fff"], domain=[0, 1], truncate=True)\
.fmt_percent(['births_per_year_growth', 'pct_male_2000_2024', 'pct_male_1980_1999', 'pct_male_change'], decimals=1)\
.tab_header(title='top unisex names from 1980-1999 that became more masculine in 2000-2024',
subtitle=md(f'{len(more_masculine_1980_1999_2000_2024)} out of {len(comparison_data_1980_1999_2000_2024)} names became more masculine.<br/>names appearing in both 2000-2024 and 1980-1999 charts are highlighted purple.'))\
.show()
| top unisex names from 1980-1999 that became more masculine in 2000-2024 |
11 out of 25 names became more masculine. names appearing in both 2000-2024 and 1980-1999 charts are highlighted purple. |
namebirths per year,
1980-1999births per year,
2000-2024births per year
growth% boys,
1980-1999% boys,
2000-2024% boys change
| Ashton | 1,120.5 | 3,284.2 | 193.1% | 45.1% | 91.2% | 46.0% |
| Jaime | 2,425.4 | 968.7 | −60.1% | 54.5% | 86.7% | 32.3% |
| Kristian | 578.6 | 483.4 | −16.4% | 59.7% | 88.0% | 28.3% |
| Darian | 520.6 | 407.2 | −21.8% | 57.4% | 85.1% | 27.8% |
| Jean | 614.6 | 282.2 | −54.1% | 45.4% | 72.9% | 27.5% |
| Jody | 555.1 | 98.4 | −82.3% | 43.4% | 68.5% | 25.1% |
| Angel | 4,451.1 | 8,868.2 | 99.2% | 59.3% | 81.9% | 22.6% |
| Kerry | 897.4 | 129.7 | −85.5% | 38.5% | 60.1% | 21.6% |
| Carey | 265.6 | 41.0 | −84.6% | 46.9% | 63.2% | 16.3% |
| Kirby | 266.1 | 86.9 | −67.3% | 51.6% | 59.9% | 8.3% |
| Casey | 5,951.7 | 1,447.6 | −75.7% | 54.8% | 61.4% | 6.6% |
All but 2 of the names that became more masculine declined in popularity, which could again reflect greater trendiness in girls’ names. The two exceptions, Ashton and Angel, were able to swim against the current because of celebrity influence and demographic change (Angel is predominately masculine in Latin America). Overall, the names that became more masculine did so because they were being given to fewer girls, but also to fewer babies overall— boys’ parents just were slower to adapt to the changing name fashions.
top 25 gender-neutral names: 1960-1980
And finally, digging even farther back, we can look at the 1960s and 1970s:
start_year = 1960
end_year = 1979
births_threshold = 5000
filtered_data_1960 = filter_name_data(name_data, start_year, births_threshold, end_year)
filtered_data_1960 = calculate_gender_neutrality(filtered_data_1960)
plot_data_1960 = filtered_data_1960.sort('diversity_index', descending=True).head(25)
fig = plot_gender_neutral_names(plot_data_1960, start_year=start_year, end_year=end_year, total_births_threshold=births_threshold)
The most striking detail that stands out when you put a chart of unisex names from the 1960s and 1970s against later periods is how much less evenly divided the “most unisex” names from 1960-1979 were compared to the decades that followed. Given that the Women’s liberation movement only rose to prominence starting in the late 1960s, it could be argued that starker division in names given to children in the 1960s and before was a product of more rigid attitudes towards gender throughout society.
conclusion: snapshots have limitations
In this post we compared trends in gender balance across aggregations covering periods of 20 years or more. Even from this high-level vantage point, it’s clear that gender-neutral names can and often do undergo dramatic shifts in gender association over time.
Gender shifts can influence how names are perceived (or stereotyped) in everyday life— for instance, a baby named Charlie is more likely to be female than a middle-aged person with that name. Trends in gendered usage mean that a name isn’t really unisex across ages. In some cases the shift is so profound that a name can become wholly linked with one gender or another— these days Ashley is definitely a girls’ name, while Ashton is definitely male. Any history of gender neutrality for these names is just a historical footnote.
Many names that show up as unisex in high-level aggregations, like the charts here, only appear that way because we’re summarizing them at a moment of transition from one predominate gender to another. If we want to find names that are truly unisex, we need a different way to measure and visualize gender neutrality over time. Stay tuned.
coda: names that aren’t really unisex (enough)
Every reddit post on the subject of gender-neutral names inevitably includes one or more comments to the effect of “what about Alex?”, or one of the other names listed below. Some of these names, like Alex and Sam, are usually used as nicknames for more gendered legal names— think Alexander or Alexandra for Alex. The Social Security Administration data doesn’t take nicknames into account.
Others, like Taylor, Morgan, and Quinn, were historically more neutral but have since become predominately female.
Robin, on the other hand, has become more masculine in recent decades, but the change is driven more by falling female births than rising male births. Robin is the name that comes closest to having made the threshold for inclusion in the original 2000-2024 chart; it ranks 29th, while the chart cuts off at 25 names.
not_really_unisex_enough_names = ['Taylor', 'Alex', 'Cameron', 'Morgan', 'Chris', 'Quinn', 'Kim', 'Robin', 'Sam', 'Pat']
not_really_unisex_enough = filtered_data.filter(pl.col('name').is_in(not_really_unisex_enough_names)).sort('diversity_index', descending=True)
fig = plot_gender_neutral_names(not_really_unisex_enough, title="names that aren't among the most unisex in the US,\ndespite what you may think, 2000-2024")
postscript: less obscure names
Ok, but what if we limit this to names that have at least 50,000 births since 2000? Surely we get some more recognizable names then?
start_year = 2000
end_year = 2024
births_threshold = 50000
n_plots = 25
filtered_data_50k = filter_name_data(name_data, start_year, births_threshold, end_year)
filtered_data_50k = calculate_gender_neutrality(filtered_data_50k)
plot_data_50k = filtered_data_50k.sort('diversity_index', descending=True).head(n_plots)
fig = plot_gender_neutral_names(plot_data_50k, start_year=start_year, total_births_threshold=births_threshold, title = f"the {n_plots} most androgynous baby names in the U.S.,\n{start_year}-{end_year} (50k min, sorted by diversity)")
We do capture a much larger proportion of the names that people usually think of as “unisex”— which popularly means “commonly used for both sexes” more than it means “used for both sexes in perfect balance.”
one last thing: truly obscure names
OK, so what if we go in the other direction— what are names with the most gender-balanced usage if we lower the limit to 500 total births?
start_year = 2000
end_year = 2024
births_threshold = 500
n_plots = 25
filtered_data_05k = filter_name_data(name_data, start_year, births_threshold, end_year)
filtered_data_05k = calculate_gender_neutrality(filtered_data_05k)
plot_data_05k = filtered_data_05k.sort('diversity_index', descending=True).head(n_plots)
fig = plot_gender_neutral_names(plot_data_05k, start_year=start_year, total_births_threshold=births_threshold, title = f"the {n_plots} most androgynous baby names in the U.S.,\n{start_year}-{end_year} (500 min, sorted by diversity)")
Well, lots of “creative” spellings appear out of the woodwork when you set such a low bar… for popularity. There’s definitely something to unpack here, but I’ll leave that as an exercise for the reader.