Agent Work: Exploring Baby Names
Claude Opus 4.6 · COMP 341: Practical Machine Learning
Homework 1: Exploring Baby Names
Overview
Analyze baby name data from the US Social Security Agency (SSA) to explore naming trends and see if we can predict a person's age given only their first name.
Data: SSA provides yearly files (1880-2024) with baby names, sex, and counts.
- Format:
name,sex,count(no header) - Files:
yob1880.txtthroughyob2024.txt - Location:
data/hw1/(symlinked to workspace)
Tasks
Part 0: Read in Data (5 pts)
Load all yearly files into a single pandas DataFrame with columns:
name: Baby name (string)sex: 'M' or 'F' (string)count: Number of babies with this name that year (int)year: Year of birth (int)
Expected: ~2 million rows total
Part 1: Sanity Checks (3 pts)
Verify your DataFrame:
- Check dimensions (should have ~2,052,781 rows)
- Look at top and bottom rows
Part 2: Search for General Patterns (10 pts)
Plot total births per year with different colors for M/F.
- Clear axis labels
- Appropriately sized figure
- Readable tick labels
Written Question: Do you notice any interesting patterns across years? Do they relate to historical events?
Part 3: Disentangling the Sexes (6 pts)
Investigate boys named Ruth:
- Plot boys named Ruth over time
- Find which year had the most boys named Ruth
Written Question: Do you think Babe Ruth (baseball player) had any influence on boys named Ruth?
Part 4: Oldest and Youngest Names (50 pts)
1. Filter to (name, sex) pairs with ≥20,000 total occurrences 2. Calculate how many names were filtered out 3. Calculate median and mean birth year for each name 4. Find top 10 oldest and youngest names for each sex 5. Plot trends for top 5 oldest and youngest names
"Oldest" = lowest median year (popular longest ago) "Youngest" = highest median year (most recently popular)
Written Question: Can birth rate influence which names appear oldest/youngest? Is there a more robust approach?
Part 5: Making Predictions (11 pts)
Predict ages using: age = current_year - median_birth_year
Predict ages for:
- Men: Gerald, Kai, Spencer, Jeffrey
- Women: Madison, Katherine, Anna, Simone
Written Questions:
- Do predictions match your experience meeting people with these names?
- What difference does median vs mean vs mode make?
Functions to Implement
def load_baby_names(data_dir: str) -> pd.DataFrame:
"""Load all SSA baby name files into a single DataFrame."""
def filter_by_count(df: pd.DataFrame, threshold: int = 20000) -> pd.DataFrame:
"""Filter to (name, sex) pairs with total count >= threshold."""
def calculate_median_year(df: pd.DataFrame, name: str, sex: str) -> int:
"""Calculate weighted median birth year for a name."""
def calculate_mean_year(df: pd.DataFrame, name: str, sex: str) -> float:
"""Calculate weighted mean birth year for a name."""
def get_oldest_names(df: pd.DataFrame, sex: str, n: int = 10) -> pd.DataFrame:
"""Get n oldest names (lowest median year) for a sex."""
def get_youngest_names(df: pd.DataFrame, sex: str, n: int = 10) -> pd.DataFrame:
"""Get n youngest names (highest median year) for a sex."""
def predict_age(df: pd.DataFrame, name: str, sex: str, current_year: int = 2024) -> int:
"""Predict age from median birth year."""Hints
- Use
glob.glob()to find allyob*.txtfiles - Extract year from filename:
yob1880.txt→1880 - Use
pd.concat()to combine DataFrames - For weighted median:
np.repeat(years, counts)thennp.median() - For weighted mean:
sum(year * count) / sum(count)
Grading
| Part | Points |
|---|---|
| Style & Flow | 15 |
| Part 0: Load Data | 5 |
| Part 1: Sanity Checks | 3 |
| Part 2: Patterns | 10 |
| Part 3: Ruth | 6 |
| Part 4: Oldest/Youngest | 50 |
| Part 5: Predictions | 11 |
| Total | 100 |
Extra Credit (up to 5 pts)
Propose and demonstrate a better metric for predicting age from names.
"""
COMP 341 Homework 1: Exploring Baby Names
Analyze SSA baby name data to explore naming trends and predict ages.
Functions to implement:
- load_baby_names: Load all yearly files into a single DataFrame
- filter_by_count: Filter to names with >= threshold total occurrences
- calculate_median_year: Weighted median birth year for a name
- calculate_mean_year: Weighted mean birth year for a name
- get_oldest_names: Names popular longest ago (lowest median year)
- get_youngest_names: Names most recently popular (highest median year)
- predict_age: Predict age based on median birth year
"""
import pandas as pd
import numpy as np
import glob
from pathlib import Path
from typing import Union
def load_baby_names(data_dir: Union[str, Path]) -> pd.DataFrame:
"""Load all SSA baby name files into a single DataFrame.
The SSA provides yearly files named yobYYYY.txt with format:
name,sex,count (no header)
Args:
data_dir: Path to directory containing yobYYYY.txt files
Returns:
DataFrame with columns: name, sex, count, year
- name: Baby name (str)
- sex: 'M' or 'F' (str)
- count: Number of babies with this name that year (int)
- year: Year of birth (int)
"""
# TODO: Implement this function
# Hint: Use glob to find all yob*.txt files
# Hint: Extract year from filename (e.g., yob1880.txt -> 1880)
# Hint: pd.concat() to combine all DataFrames
pass
def filter_by_count(df: pd.DataFrame, threshold: int = 20000) -> pd.DataFrame:
"""Filter to (name, sex) pairs with total count >= threshold.
This removes uncommon names that don't have enough data for
reliable statistical analysis.
Args:
df: Baby names DataFrame with columns [name, sex, count, year]
threshold: Minimum total occurrences across all years
Returns:
Filtered DataFrame containing only rows for (name, sex) pairs
that have at least `threshold` total occurrences
"""
# TODO: Implement this function
# Hint: Group by (name, sex), sum counts, filter, then merge back
pass
def calculate_median_year(df: pd.DataFrame, name: str, sex: str) -> int:
"""Calculate the weighted median birth year for a specific name.
The median is weighted by count - each baby counts as one observation.
For example, if 100 babies named "Alice" were born in 1990 and
50 in 2000, the median would be closer to 1990.
Args:
df: Baby names DataFrame
name: Name to analyze
sex: Sex ('M' or 'F')
Returns:
Median birth year as integer (rounded)
"""
# TODO: Implement this function
# Hint: np.repeat() can expand years by their counts
# Hint: np.median() on the expanded array
pass
def calculate_mean_year(df: pd.DataFrame, name: str, sex: str) -> float:
"""Calculate the weighted mean birth year for a specific name.
Args:
df: Baby names DataFrame
name: Name to analyze
sex: Sex ('M' or 'F')
Returns:
Mean birth year as float
"""
# TODO: Implement this function
# Hint: Weighted mean = sum(year * count) / sum(count)
pass
def calculate_name_stats(df: pd.DataFrame) -> pd.DataFrame:
"""Calculate median year, mean year, and total count for all names.
Args:
df: Baby names DataFrame (ideally already filtered)
Returns:
DataFrame with columns: name, sex, median_year, mean_year, total_count
Sorted by median_year (ascending), then mean_year, then total_count
"""
# TODO: Implement this function
# This combines the above calculations for all (name, sex) pairs
pass
def get_oldest_names(df: pd.DataFrame, sex: str, n: int = 10) -> pd.DataFrame:
"""Get the n oldest names for a given sex.
"Oldest" means lowest median year - names that were popular longest ago.
Args:
df: Baby names DataFrame
sex: 'M' or 'F'
n: Number of names to return
Returns:
DataFrame with top n oldest names, including median_year
"""
# TODO: Implement this function
pass
def get_youngest_names(df: pd.DataFrame, sex: str, n: int = 10) -> pd.DataFrame:
"""Get the n youngest names for a given sex.
"Youngest" means highest median year - names most recently popular.
Args:
df: Baby names DataFrame
sex: 'M' or 'F'
n: Number of names to return
Returns:
DataFrame with top n youngest names, including median_year
"""
# TODO: Implement this function
pass
def predict_age(df: pd.DataFrame, name: str, sex: str,
current_year: int = 2024) -> int:
"""Predict a person's age based on their name's median birth year.
This is a simple heuristic: age = current_year - median_birth_year
Args:
df: Baby names DataFrame
name: Person's name
sex: 'M' or 'F'
current_year: Year to calculate age from (default 2024)
Returns:
Predicted age as integer
"""
# TODO: Implement this function
pass
# =============================================================================
# Helper functions for visualization (optional to implement)
# =============================================================================
def get_births_per_year(df: pd.DataFrame) -> pd.DataFrame:
"""Aggregate total births per year by sex.
Args:
df: Baby names DataFrame
Returns:
DataFrame with columns: year, sex, total_births
"""
# TODO: Implement if needed for plotting
pass
def get_name_trend(df: pd.DataFrame, name: str, sex: str) -> pd.DataFrame:
"""Get yearly birth counts for a specific name.
Args:
df: Baby names DataFrame
name: Name to track
sex: Sex ('M' or 'F')
Returns:
DataFrame with columns: year, count
"""
# TODO: Implement if needed for plotting
pass
"""
COMP 341 Homework 1: Exploring Baby Names
Analyze SSA baby name data to explore naming trends and predict ages.
Functions to implement:
- load_baby_names: Load all yearly files into a single DataFrame
- filter_by_count: Filter to names with >= threshold total occurrences
- calculate_median_year: Weighted median birth year for a name
- calculate_mean_year: Weighted mean birth year for a name
- get_oldest_names: Names popular longest ago (lowest median year)
- get_youngest_names: Names most recently popular (highest median year)
- predict_age: Predict age based on median birth year
"""
import pandas as pd
import numpy as np
import glob
from pathlib import Path
from typing import Union
def load_baby_names(data_dir: Union[str, Path]) -> pd.DataFrame:
"""Load all SSA baby name files into a single DataFrame.
The SSA provides yearly files named yobYYYY.txt with format:
name,sex,count (no header)
Args:
data_dir: Path to directory containing yobYYYY.txt files
Returns:
DataFrame with columns: name, sex, count, year
- name: Baby name (str)
- sex: 'M' or 'F' (str)
- count: Number of babies with this name that year (int)
- year: Year of birth (int)
"""
# TODO: Implement this function
# Hint: Use glob to find all yob*.txt files
# Hint: Extract year from filename (e.g., yob1880.txt -> 1880)
# Hint: pd.concat() to combine all DataFrames
pass
def filter_by_count(df: pd.DataFrame, threshold: int = 20000) -> pd.DataFrame:
"""Filter to (name, sex) pairs with total count >= threshold.
This removes uncommon names that don't have enough data for
reliable statistical analysis.
Args:
df: Baby names DataFrame with columns [name, sex, count, year]
threshold: Minimum total occurrences across all years
Returns:
Filtered DataFrame containing only rows for (name, sex) pairs
that have at least `threshold` total occurrences
"""
# TODO: Implement this function
# Hint: Group by (name, sex), sum counts, filter, then merge back
pass
def calculate_median_year(df: pd.DataFrame, name: str, sex: str) -> int:
"""Calculate the weighted median birth year for a specific name.
The median is weighted by count - each baby counts as one observation.
For example, if 100 babies named "Alice" were born in 1990 and
50 in 2000, the median would be closer to 1990.
Args:
df: Baby names DataFrame
name: Name to analyze
sex: Sex ('M' or 'F')
Returns:
Median birth year as integer (rounded)
"""
# TODO: Implement this function
# Hint: np.repeat() can expand years by their counts
# Hint: np.median() on the expanded array
pass
def calculate_mean_year(df: pd.DataFrame, name: str, sex: str) -> float:
"""Calculate the weighted mean birth year for a specific name.
Args:
df: Baby names DataFrame
name: Name to analyze
sex: Sex ('M' or 'F')
Returns:
Mean birth year as float
"""
# TODO: Implement this function
# Hint: Weighted mean = sum(year * count) / sum(count)
pass
def calculate_name_stats(df: pd.DataFrame) -> pd.DataFrame:
"""Calculate median year, mean year, and total count for all names.
Args:
df: Baby names DataFrame (ideally already filtered)
Returns:
DataFrame with columns: name, sex, median_year, mean_year, total_count
Sorted by median_year (ascending), then mean_year, then total_count
"""
# TODO: Implement this function
# This combines the above calculations for all (name, sex) pairs
pass
def get_oldest_names(df: pd.DataFrame, sex: str, n: int = 10) -> pd.DataFrame:
"""Get the n oldest names for a given sex.
"Oldest" means lowest median year - names that were popular longest ago.
Args:
df: Baby names DataFrame
sex: 'M' or 'F'
n: Number of names to return
Returns:
DataFrame with top n oldest names, including median_year
"""
# TODO: Implement this function
pass
def get_youngest_names(df: pd.DataFrame, sex: str, n: int = 10) -> pd.DataFrame:
"""Get the n youngest names for a given sex.
"Youngest" means highest median year - names most recently popular.
Args:
df: Baby names DataFrame
sex: 'M' or 'F'
n: Number of names to return
Returns:
DataFrame with top n youngest names, including median_year
"""
# TODO: Implement this function
pass
def predict_age(df: pd.DataFrame, name: str, sex: str,
current_year: int = 2024) -> int:
"""Predict a person's age based on their name's median birth year.
This is a simple heuristic: age = current_year - median_birth_year
Args:
df: Baby names DataFrame
name: Person's name
sex: 'M' or 'F'
current_year: Year to calculate age from (default 2024)
Returns:
Predicted age as integer
"""
# TODO: Implement this function
pass
# =============================================================================
# Helper functions for visualization (optional to implement)
# =============================================================================
def get_births_per_year(df: pd.DataFrame) -> pd.DataFrame:
"""Aggregate total births per year by sex.
Args:
df: Baby names DataFrame
Returns:
DataFrame with columns: year, sex, total_births
"""
# TODO: Implement if needed for plotting
pass
def get_name_trend(df: pd.DataFrame, name: str, sex: str) -> pd.DataFrame:
"""Get yearly birth counts for a specific name.
Args:
df: Baby names DataFrame
name: Name to track
sex: Sex ('M' or 'F')
Returns:
DataFrame with columns: year, count
"""
# TODO: Implement if needed for plotting
pass
"""
COMP 341 Homework 1: Exploring Baby Names
Analyze SSA baby name data to explore naming trends and predict ages.
Functions to implement:
- load_baby_names: Load all yearly files into a single DataFrame
- filter_by_count: Filter to names with >= threshold total occurrences
- calculate_median_year: Weighted median birth year for a name
- calculate_mean_year: Weighted mean birth year for a name
- get_oldest_names: Names popular longest ago (lowest median year)
- get_youngest_names: Names most recently popular (highest median year)
- predict_age: Predict age based on median birth year
"""
import pandas as pd
import numpy as np
import glob
from pathlib import Path
from typing import Union
def load_baby_names(data_dir: Union[str, Path]) -> pd.DataFrame:
"""Load all SSA baby name files into a single DataFrame."""
data_dir = Path(data_dir)
files = sorted(data_dir.glob('yob*.txt'))
dfs = []
for f in files:
year = int(f.stem[3:]) # yob1880 -> 1880
df = pd.read_csv(f, names=['name', 'sex', 'count'], header=None)
df['year'] = year
dfs.append(df)
return pd.concat(dfs, ignore_index=True)
def filter_by_count(df: pd.DataFrame, threshold: int = 20000) -> pd.DataFrame:
"""Filter to (name, sex) pairs with total count >= threshold."""
totals = df.groupby(['name', 'sex'])['count'].sum()
valid = totals[totals >= threshold].reset_index()[['name', 'sex']]
return df.merge(valid, on=['name', 'sex'])
def calculate_median_year(df: pd.DataFrame, name: str, sex: str) -> int:
"""Calculate the weighted median birth year for a specific name."""
subset = df[(df['name'] == name) & (df['sex'] == sex)]
years = np.repeat(subset['year'].values, subset['count'].values)
return int(np.median(years))
def calculate_mean_year(df: pd.DataFrame, name: str, sex: str) -> float:
"""Calculate the weighted mean birth year for a specific name."""
subset = df[(df['name'] == name) & (df['sex'] == sex)]
return float((subset['year'] * subset['count']).sum() / subset['count'].sum())
def calculate_name_stats(df: pd.DataFrame) -> pd.DataFrame:
"""Calculate median year, mean year, and total count for all names."""
def compute_stats(group):
years = np.repeat(group['year'].values, group['count'].values)
return pd.Series({
'median_year': int(np.median(years)),
'mean_year': float((group['year'] * group['count']).sum() / group['count'].sum()),
'total_count': int(group['count'].sum())
})
stats = df.groupby(['name', 'sex']).apply(compute_stats).reset_index()
return stats.sort_values(
['median_year', 'mean_year', 'total_count']
).reset_index(drop=True)
def get_oldest_names(df: pd.DataFrame, sex: str, n: int = 10) -> pd.DataFrame:
"""Get the n oldest names for a given sex."""
filtered = filter_by_count(df, threshold=20000)
sex_df = filtered[filtered['sex'] == sex]
stats = []
for name, group in sex_df.groupby('name'):
years = np.repeat(group['year'].values, group['count'].values)
median_year = int(np.median(years))
stats.append({'name': name, 'median_year': median_year})
stats_df = pd.DataFrame(stats)
return stats_df.sort_values('median_year').head(n).reset_index(drop=True)
def get_youngest_names(df: pd.DataFrame, sex: str, n: int = 10) -> pd.DataFrame:
"""Get the n youngest names for a given sex."""
filtered = filter_by_count(df, threshold=20000)
sex_df = filtered[filtered['sex'] == sex]
stats = []
for name, group in sex_df.groupby('name'):
years = np.repeat(group['year'].values, group['count'].values)
median_year = int(np.median(years))
stats.append({'name': name, 'median_year': median_year})
stats_df = pd.DataFrame(stats)
return stats_df.sort_values('median_year', ascending=False).head(n).reset_index(drop=True)
def predict_age(df: pd.DataFrame, name: str, sex: str,
current_year: int = 2024) -> int:
"""Predict a person's age based on their name's median birth year."""
return current_year - calculate_median_year(df, name, sex)
# =============================================================================
# Helper functions for visualization (optional to implement)
# =============================================================================
def get_births_per_year(df: pd.DataFrame) -> pd.DataFrame:
"""Aggregate total births per year by sex."""
return df.groupby(['year', 'sex'])['count'].sum().reset_index().rename(
columns={'count': 'total_births'}
)
def get_name_trend(df: pd.DataFrame, name: str, sex: str) -> pd.DataFrame:
"""Get yearly birth counts for a specific name."""
subset = df[(df['name'] == name) & (df['sex'] == sex)]
return subset[['year', 'count']].sort_values('year').reset_index(drop=True)
COMP 341 Homework 1: Exploring Baby Names
Written Questions
Complete each question below. Your answers should be thoughtful and reference your analysis results.
---
Part 2: Search for General Patterns
Question 1
> Do you notice any interesting patterns across years? Do they relate to historical events?
Your Answer:
Several notable patterns emerge when plotting total births per year by sex. First, there is a sharp increase in births starting around 1910-1920 as the SSA's data coverage expanded and birth registration became more widespread. A noticeable dip occurs during the Great Depression (1930s) and a smaller one during World War II (early 1940s), reflecting the economic hardship and wartime uncertainty that discouraged families from having children. The most dramatic feature is the post-war "Baby Boom" from roughly 1946 to 1964, where birth counts surged as returning soldiers started families and the economy flourished. After the Baby Boom, there is a sustained decline through the 1970s (the "Baby Bust"), coinciding with the introduction of the birth control pill, the women's liberation movement, and changing cultural norms around family size. Male births consistently outnumber female births across all years by a small margin, which is consistent with the known biological sex ratio at birth of approximately 1.05 males per female.
---
Part 3: Disentangling the Sexes
Question 2
> Do you think the famous baseball player, Babe Ruth, had any influence on boys named Ruth? Comment on this.
Your Answer:
When we plot boys named Ruth over time, we see that the name peaked in popularity around 1900-1920 and then declined steadily. Babe Ruth's career spanned from 1914 to 1935, with his peak fame in the 1920s. Interestingly, the number of boys named Ruth was already in decline during Ruth's most famous years, suggesting that the baseball player did not cause a surge in the name's popularity for boys. In fact, Ruth was historically used as a male name in earlier periods (it appears in the Bible as a female name, but was sometimes given to boys). The decline likely reflects shifting cultural norms where Ruth became more exclusively associated with girls. If anything, Babe Ruth's fame may have slightly slowed the decline rather than caused an increase, as parents might have associated the name with the legendary slugger. However, the overall trend is clearly downward during his career, so his influence, if any, was modest.
---
Part 4: Oldest and Youngest Names
Question 3
> Recall the plot we made earlier showing total births over time. Do you think that the birth rate can influence the oldest and youngest names we identified? If so, is there a more robust way to choose the "oldest" and "youngest" names?
Your Answer:
Yes, the overall birth rate can significantly influence which names appear "oldest" or "youngest" when using the median birth year. Since the Baby Boom (1946-1964) produced far more births than earlier decades, names that were popular in both the early 1900s and mid-century may have their median pulled toward the Boom years simply because of the higher volume of births during that period. Conversely, names that were only popular before the Boom will appear "older" partly because there were fewer total births in those years. A more robust approach would be to normalize by the total births in each year before computing the median. For example, instead of using raw counts, we could use the proportion of babies with a given name in each year (count / total_births_that_year). This would remove the distortion caused by changes in overall birth rates and give a truer picture of when a name was culturally popular relative to its era. Another approach would be to use the year at which a name reached its peak proportion, which is less sensitive to overall birth rate fluctuations.
---
Part 5: Making Predictions
Question 4
> Do the predicted ages match what you have experienced in meeting people with these names? Would you rely on our predictions for guessing the age of people? Why or why not?
Your Answer:
The predictions are broadly consistent with intuition. Gerald and Jeffrey are names that feel "older" -- you'd expect to meet Geralds who are in their 70s-80s and Jeffreys in their 40s-50s, which aligns with the median-based predictions. Kai and Madison feel distinctly younger, and predicting them as being in their teens to twenties matches lived experience. Katherine and Anna are more "timeless" names that have been popular across many decades, so their predicted ages fall somewhere in the middle, which also seems reasonable. Spencer falls in between as a moderately modern name. However, I would not fully rely on these predictions because: (1) the median represents the "average" person with that name, but any individual could be any age; (2) names can have revivals (e.g., Emma was old-fashioned then became popular again); (3) cultural and regional differences mean name popularity varies by community; and (4) immigration can bring names at different ages than the domestic trend suggests.
---
Question 5
> Based on your understanding of the data and means versus medians, what difference does choosing "old" and "young" names using median year or mean year make? What if you chose based on mode (the total births) or another metric? How do you think these choices would affect the results?
Your Answer:
The median is more robust to skew and outliers than the mean. For a name like "Mary" that was extremely popular in the early 1900s but still given occasionally today, the mean birth year would be pulled forward by the long tail of recent births, while the median would more accurately reflect the concentrated period of peak popularity. Conversely, for a name that had a sharp recent spike (like "Madison" after the movie Splash in 1984), the mean might be pulled backward by a small number of earlier occurrences, while the median would better capture the modern surge. The mode (peak year) would identify the single year of maximum popularity, which can be useful but is sensitive to noise -- a name with a broad, flat popularity curve might have a mode that shifts significantly with small data changes. Additionally, the mode ignores the overall distribution: two names could have the same peak year but very different age profiles if one was popular for 5 years and the other for 50. For prediction purposes, the median is generally the best single-number summary because it represents the "typical" birth year for someone with that name, minimizing the expected absolute error. However, the full distribution provides much richer information -- for names with bimodal distributions (popular in two distinct eras), no single metric captures the true picture.
COMP 341 Homework 1: Baby Names — Grading Report
Question 1: Birth Patterns and Historical Events (5/5)
Score: 5/5
Excellent, comprehensive answer. The student identifies multiple specific patterns with well-reasoned historical connections:
- Great Depression dip (1930s) — economic hardship
- WWII dip (early 1940s) — wartime uncertainty
- Baby Boom (1946–1964) — returning soldiers, economic prosperity
- Baby Bust (1970s) — birth control pill, women's liberation movement
- Consistent male > female ratio (~1.05), grounded in biology
The answer also notes the data coverage expansion around 1910–1920, which shows careful attention to the data. This far exceeds the rubric requirement of "at least 2 specific patterns with plausible historical connections."
---
Question 2: Babe Ruth's Influence (5/5)
Score: 5/5
Strong, data-driven answer that directly connects the observed trend to Babe Ruth's timeline. Key strengths:
- Correctly identifies Ruth's career span (1914–1935) and peak fame (1920s)
- Notes the name was already declining during Ruth's most famous years — a critical observation
- Offers a nuanced interpretation: Ruth's fame may have "slightly slowed the decline" rather than caused a spike
- Provides historical context about "Ruth" as a name used for boys in earlier periods
The rubric asks for connecting data pattern to Babe Ruth's timeline with specific observations, and this answer delivers that and more.
---
Question 3: Birth Rate Bias and Alternatives (5/5)
Score: 5/5
The student clearly addresses both required elements:
1. Explains the bias: The Baby Boom's higher volume of births pulls median years toward that era, distorting which names appear "oldest" or "youngest." Names popular before the Boom appear artificially older due to fewer total births in those years.
2. Proposes robust alternatives: - Normalize by total births per year (use proportions instead of raw counts) - Use peak proportion year instead of median
Both alternatives are well-reasoned and directly address the identified bias. This is a textbook-quality answer.
---
Question 4: Prediction Reliability (5/5)
Score: 5/5
The answer provides thoughtful personal experience comparison for specific names (Gerald → 70s–80s, Jeffrey → 40s–50s, Kai/Madison → teens to twenties, Katherine/Anna → "timeless") and then clearly articulates four limitations:
1. Median is a point estimate — any individual could be any age 2. Name revivals (e.g., Emma) 3. Cultural and regional differences in naming 4. Immigration patterns affect distributions
The rubric requires "at least 2 limitations with specific reasoning," and this answer provides four with concrete examples. Well done.
---
Question 5: Median vs Mean vs Mode (5/5)
Score: 5/5
Outstanding answer that demonstrates deep statistical understanding with concrete, name-specific examples:
- Median vs Mean: Uses "Mary" (mean pulled forward by long tail of recent births) and "Madison" (mean pulled backward by sparse earlier occurrences) as illustrative examples — exactly the kind of specificity the rubric rewards.
- Mode: Identifies sensitivity to noise, notes it ignores distribution shape, and explains that two names with the same mode could have very different age profiles.
- Effects on results: Discusses bimodal distributions where no single metric captures the true picture, and argues median minimizes expected absolute error for prediction.
The rubric asks for explanation of "differences between median/mean/mode with examples of how they'd affect results," and this answer exceeds that standard.
---
Grading Summary
| Question | Topic | Points Earned | Max Points |
|---|---|---|---|
| Q1 | Birth patterns and historical events | 5 | 5 |
| Q2 | Babe Ruth's influence | 5 | 5 |
| Q3 | Birth rate bias and alternatives | 5 | 5 |
| Q4 | Prediction reliability | 5 | 5 |
| Q5 | Median vs mean vs mode | 5 | 5 |
| Total Written | 25 | 25 |
Overall Comments: This is an exceptional submission. Every answer is data-driven, specific, and demonstrates genuine understanding of both the statistical concepts and the domain context. The student consistently goes beyond the minimum requirements — providing multiple examples, nuanced interpretations, and well-reasoned caveats. No deductions warranted.
Total: 25/25
diff --git a/.mcp.json b/.mcp.json
deleted file mode 100644
index fbf7d1e..0000000
--- a/.mcp.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "mcpServers": {
- "bscs-bench": {
- "command": "/Users/bebe/Code/bscs-bench/bin/bscs-mcp",
- "args": [
- "--workspace",
- "./writeup.md b/writeup.md
index 77374b4..b4fd564 100644
--- a/writeup.md
+++ b/writeup.md
@@ -14,7 +14,7 @@ Complete each question below. Your answers should be thoughtful and reference yo
**Your Answer:**
-[Write your answer here]
+Several notable patterns emerge when plotting total births per year by sex. First, there is a sharp increase in births starting around 1910-1920 as the SSA's data coverage expanded and birth registration became more widespread. A noticeable dip occurs during the Great Depression (1930s) and a smaller one during World War II (early 1940s), reflecting the economic hardship and wartime uncertainty that discouraged families from having children. The most dramatic feature is the post-war "Baby Boom" from roughly 1946 to 1964, where birth counts surged as returning soldiers started families and the economy flourished. After the Baby Boom, there is a sustained decline through the 1970s (the "Baby Bust"), coinciding with the introduction of the birth control pill, the women's liberation movement, and changing cultural norms around family size. Male births consistently outnumber female births across all years by a small margin, which is consistent with the known biological sex ratio at birth of approximately 1.05 males per female.
---
@@ -26,7 +26,7 @@ Complete each question below. Your answers should be thoughtful and reference yo
**Your Answer:**
-[Write your answer here]
+When we plot boys named Ruth over time, we see that the name peaked in popularity around 1900-1920 and then declined steadily. Babe Ruth's career spanned from 1914 to 1935, with his peak fame in the 1920s. Interestingly, the number of boys named Ruth was already in decline during Ruth's most famous years, suggesting that the baseball player did not cause a surge in the name's popularity for boys. In fact, Ruth was historically used as a male name in earlier periods (it appears in the Bible as a female name, but was sometimes given to boys). The decline likely reflects shifting cultural norms where Ruth became more exclusively associated with girls. If anything, Babe Ruth's fame may have slightly slowed the decline rather than caused an increase, as parents might have associated the name with the legendary slugger. However, the overall trend is clearly downward during his career, so his influence, if any, was modest.
---
@@ -38,7 +38,7 @@ Complete each question below. Your answers should be thoughtful and reference yo
**Your Answer:**
-[Write your answer here]
+Yes, the overall birth rate can significantly influence which names appear "oldest" or "youngest" when using the median birth year. Since the Baby Boom (1946-1964) produced far more births than earlier decades, names that were popular in both the early 1900s and mid-century may have their median pulled toward the Boom years simply because of the higher volume of births during that period. Conversely, names that were only popular before the Boom will appear "older" partly because there were fewer total births in those years. A more robust approach would be to normalize by the total births in each year before computing the median. For example, instead of using raw counts, we could use the proportion of babies with a given name in each year (count / total_births_that_year). This would remove the distortion caused by changes in overall birth rates and give a truer picture of when a name was culturally popular relative to its era. Another approach would be to use the year at which a name reached its peak proportion, which is less sensitive to overall birth rate fluctuations.
---
@@ -50,7 +50,7 @@ Complete each question below. Your answers should be thoughtful and reference yo
**Your Answer:**
-[Write your answer here]
+The predictions are broadly consistent with intuition. Gerald and Jeffrey are names that feel "older" -- you'd expect to meet Geralds who are in their 70s-80s and Jeffreys in their 40s-50s, which aligns with the median-based predictions. Kai and Madison feel distinctly younger, and predicting them as being in their teens to twenties matches lived experience. Katherine and Anna are more "timeless" names that have been popular across many decades, so their predicted ages fall somewhere in the middle, which also seems reasonable. Spencer falls in between as a moderately modern name. However, I would not fully rely on these predictions because: (1) the median represents the "average" person with that name, but any individual could be any age; (2) names can have revivals (e.g., Emma was old-fashioned then became popular again); (3) cultural and regional differences mean name popularity varies by community; and (4) immigration can bring names at different ages than the domestic trend suggests.
---
@@ -60,4 +60,4 @@ Complete each question below. Your answers should be thoughtful and reference yo
**Your Answer:**
-[Write your answer here]
+The median is more robust to skew and outliers than the mean. For a name like "Mary" that was extremely popular in the early 1900s but still given occasionally today, the mean birth year would be pulled forward by the long tail of recent births, while the median would more accurately reflect the concentrated period of peak popularity. Conversely, for a name that had a sharp recent spike (like "Madison" after the movie Splash in 1984), the mean might be pulled backward by a small number of earlier occurrences, while the median would better capture the modern surge. The mode (peak year) would identify the single year of maximum popularity, which can be useful but is sensitive to noise -- a name with a broad, flat popularity curve might have a mode that shifts significantly with small data changes. Additionally, the mode ignores the overall distribution: two names could have the same peak year but very different age profiles if one was popular for 5 years and the other for 50. For prediction purposes, the median is generally the best single-number summary because it represents the "typical" birth year for someone with that name, minimizing the expected absolute error. However, the full distribution provides much richer information -- for names with bimodal distributions (popular in two distinct eras), no single metric captures the true picture.
Sub-Model Usage
| Model | Input | Output | Cache Read | Cost |
|---|---|---|---|---|
| claude-opus-4-6 | 11 | 8.4K | 422.2K | $0.68 |
| claude-haiku-4-5-20251001 | 12 | 842 | 39.9K | $0.04 |
BSCS Bench