Lab 6: Taste Data

The goal of this lab is to reflect on cultural data exploration, with the first-names data discussed by Lieberson as an example. We will use R, but you are not expected to figure out all the details of the code used here. Instead, I will give you code examples to copy over and modify. What is important here is not that you get everything about R today but that you think and talk about what these data manipulations show us about culture, and how they compare to other possible approaches to the same intellectual problems.

Get set up

Start RStudio. Load the data and extra functions we will be using with the following lines:

Note to visitors in 2023 and after: since I updated my dataculture package, to run the code below you will also need to explicitly install and load the tidyverse and babynames packages from CRAN.

library(tidyverse)
library(dataculture)
library(babynames)

(1) Make a chart

Here is how to generate a chart I showed on Monday:

marilyn <- babynames |> filter(name == "Marilyn", sex == "F")

ggplot(marilyn, aes(x=year, y=prop)) + geom_line()

Copy this into R and see what you get.

The first line is an assignment using <- and the “pipe” |>. It says to take only rows of babynames where the name is Marilyn and the sex is F and to store those rows in the variable marilyn. The second line says to make a line plot using the data in marilyn, specifically the year and prop columns.

  1. Describe the information on this chart. What do the axes mean? What does any given point on the chart mean?

  2. Describe the interpretation of the chart. What does it tell you? What further information do you want or need?

Now consider this chart:

ggplot(marilyn, aes(x=year, y=n)) + geom_line()
  1. What’s the difference from the first one? To figure it out, look at the value of the data frame marilyn. Why would you choose prop or n?

  2. In your group, come up with at least three girls’ names to explore. Make charts just like the first one. (What do you have to change in the code above?) Try to surprise yourselves! Yes, you can look for your own names—and consider the question of missing data.

  3. What do you have to change to look at boys’ names? Look at the value of the data frame babynames for a hint. Make a line chart showing the proportion of boys named “Franklin.” (What’s going on there?)

(2) What is this data?

Start by looking at the documentation in R:

help(babynames)
  1. Discuss what this tells you and does not tell you about the origins of the data.

Consider a random row from the dataset:

babynames |> slice_sample(n=1)
  1. Whose names is this describing? If our research subject is “tastes for names in the US,” does it describe the ideal data point? What might the limitations be?

  2. The author of the babynames package, Hadley Wickham, is in fact unusually careful about documenting his work (he is a statistician by training). Visit the package home page at github.com/hadley/babynames. Follow links as necessary. What does this tell you about what the data represent?

The following code tallies up the total number of individuals recorded for each birth year in the babynames data:

totals <- babynames |> group_by(year) |>
    summarize(total=sum(n))

group_by does not change the data except to mark it as organized by year. summarize(n=sum(n)) says: “For every group, figure out sum(n) and produce a table with one total for each year.

  1. Now look at the applicants and births variables (use help to find out what they are). A line like totals |> filter(year == 1940) picks out the row(s) of totals for which year equals 1940. Use lines like these to compare the values of totals, applicants, and births for the years 1900, 1940, and 2000. What’s going on?

  2. Why do you think Wickham chose applicant totals as the denominators for calculating the prop column of babynames? Should he have used births instead?

The following code produces a chart of the total number of male and female individuals recorded for each birth year in the babynames data:

totals_sexes <- babynames |> group_by(year, sex) |>
    summarize(n=sum(n))

ggplot(totals_sexes, aes(x=year, y=n, color=sex)) +
    geom_line()

Note the use of summarize applied to the groups by year and sex. Having more than one category for color automatically creates more than one line here.

  1. Focusing attention on the years from 1880 to 1930, what do you notice? Can you think of any explanations? Should we treat all years of the Social Security Names data in the same way?

(3) When does taste matter? For whom?

It is amusing to look at popular names in any given year. The most recent year in babynames is 2017.

babynames |> filter(year == 2017, sex == "F") |>
    slice_max(prop, n=5)
  1. This slice_max function call “slices” off the rows with the top 5 values of prop.

    1. To verify you understand this, try changing n=5 to n=3 and compare what is similar and different.

    2. Now change the code so that you are looking at top boys’ names.

  2. Reflect: how are important are “top” names? What would you need to know in order to interpret them the way Lieberson does?

Consider the following:

top20 <- babynames |> group_by(year, sex) |>
    slice_max(prop, n=20)
top20_prop <- top20 |> summarize(prop=sum(prop))
  1. The first line uses group_by before slice_max, so it takes the top 20 rows for each group of rows with a given year and sex. The second line calculates the sum of the proportions for the rows in each group. What does this number mean?

Now try

[r} ggplot(top20_prop, aes(year, prop, color=sex)) + geom_line()

  1. Discuss the implications (recall Lieberson). (Does the choice of 20 as the cutoff matter? Try with the top 100 names instead.)

Names are a good example of something that is distributed very unevenly. Returning to 2017 girls’ names,

babynames |> filter(year == 2017, sex == "F") |>
    summarize(max_prop=max(prop))

Since there is no group_by, the summarize function calculates over all the rows of its input. In this case, max finds the maximum proportion.

  1. The mean function calculates averages. What is the average name frequency for a given girl’s name in 2017 (in the data)? Is this typical? Can you use filter and summarize to figure out what proportion of girls born in 2017 had names more frequent than this average?

The following, by contrast, calculates how many names are more frequent than this average:

girls2017names <- babynames |> filter(year == 2017, sex == "F")
girls2017above_mean <- girls2017names |>
    filter(prop > mean(prop))
nrow(girls2017above_mean) / nrow(girls2017names)

nrow(x) counts how many rows are in a data frame x.1

  1. Why are the two numbers so different?

(4) “Reading” a name

Try the following:

nn <- babynames |> filter(year >= 1940) |>
    filter(name %in% c("Dana", "Leslie", "Taylor", "Robin"))

ggplot(nn, aes(year, prop, color=sex)) +
    geom_line() +
    facet_wrap(vars(name))

The second filter says to keep only those rows where name is one of the options inside the parentheses after c(). The other new thing is facet_wrap(vars(name)) which says to make one plot for each name (these are called “small multiples”).

  1. Make sure you can explain what the figures show. (Why do I have you starting at 1940 here?) How would you describe the pattern?

  2. Add some more names to test whether the pattern you described always holds. Try “Jackie,” “Casey,” and “Angel” (see if you can think of other examples).

(5) Now you

  1. Brainstorm with your group some more social-cultural questions which you think you could investigate with this data. (Suggestion: names are made out of letters.)

  2. Brainstorm what data you would want to add to learn more.

(6) Bonus: Jersey style!

babynames contains data up to 2017, but as it happens the Social Security name data is in easily-digestible format on the SSA’s own website, with four more years of data are now available, and baby name data by state. I could not locate matching data on total numbers of applicants, so I have instead calculated proportions using data on live births through 2020 from the CDC. I have made these data available in the dataculture package under the names recent_ssa_names and recent_ssa_state_names. You have to update the package to get this data. To do this, first choose “Restart R” from the “Session” menu. Then enter

remotes::install_github("agoldst/dataculture")

to download the updated package. Now once again

library(dataculture)

Here is an example to work through on geographic variation.2

Let’s focus attention on a single year, 2019 (2020 is special, as we all know).

nj19 <- recent_ssa_state_names |>
    filter(year == 2019, state == "NJ")

natl19 <- recent_ssa_names |>
    filter(year == 2019)
  1. Look at the most frequent names for NJ and nationally in 2019. Is there much of a difference?

We want to identify names that are more or less popular in NJ than nationally. To do that, we need a joined table, which has both the national proportion and the NJ proportion for each name. Here is how to produce one:

njus19 <- nj19 |> select(name, sex, nj_prop=prop) |> 
    inner_join(natl19 |> select(name, sex, natl_prop=prop),
               by=c("name", "sex"))

Let’s put all the names on a scatterplot:

ggplot(njus19, aes(natl_prop, nj_prop, label=name)) + geom_text()

This visualization gives a lot of weight to those very popular names. We can do better:

njus19 |>
    mutate(ratio=nj_prop / natl_prop) |>
    ggplot(aes(ratio, nj_prop)) + geom_text(aes(label=name)) +
        scale_x_log10(name="(NJ freq. / US freq.)") +
        scale_y_log10(name="NJ frequency")

Here I have used mutate to add a new column to the joined table, ratio, which is calculated on a row-by-row basis. Thus the y axis gives frequency in New Jersey: popular names are toward the top. The x axis shows the ratio of New Jersey frequency to national frequency; x=1 means parity.3 The scale_x_log10 and scale_y_log10 adjust the plot to use a logarithmic rather than a linear scale on each axis.

Use the “Zoom” button in the plot viewer in RStudio to see more detail here.

  1. What patterns do you notice in the more prevalent NJ names? What do these patterns suggest about the way naming taste works? Do you see patterns in the less prevalent names?

  2. Repeat the analysis for another state. Formulate hypotheses in advance, then test them visually.


  1. Bonus R-expertise tip: actually the same result can be obtained with girls2017names |> summarize(mean(prop > mean(prop))).↩︎

  2. My analysis here is inspired by a series of visualizations by the French sociologist Baptiste Coulmont, comparing the first names of all French people with the first names of elected officials in France.↩︎

  3. Calculating the “parity” point is fiddly. The problem is choosing the right denominator for calculating the national and state-level frequency. The naive approach, just totaling up the counts for all the names, is problematic because the SSA omits rare names (n < 5) from the data for privacy reasons. Names which occur fewer than five times in NJ in a given year may well be included in the national set if they occur more than five times nationally. This means the total of the counts nationally is not as downwardly biased as the total of the counts for NJ. So instead I chose a baseline that would be comparable on the national and state levels, the number of live births. In truth, this is only a matter of the vertical/horizontal scaling of the chart, and for purposes of identifying outlier names it doesn’t matter. But “Liam” really was just about as popular in New Jersey in 2019 as it was in the whole US.↩︎