video

Do more with R: Quick lookup tables using named vectors

Learn how named vectors give R developers an easy to use key-value pairs

What’s the state abbreviation for Arkansas? Is it AR? AK? AS?

Maybe you’ve got a data frame with the information. Or any info where there’s one column with categories, and another column with values. Chances are, at some point you’d like to look up the value by category, sometimes known as the key. A lot of programming languages have ways to work with key-value pairs. This is easy to do in R, too, with named vectors. Here’s how.

I’ve got data with state names and abbreviations, which I've stored in a data frame named postal_df. (The code to create that data frame is at the bottom of this post if you'd like to follow along).

I'll run tail(postal_df) to see what that looks like.

           State PostalCode
45       Vermont         VT
46      Virginia         VA
47    Washington         WA
48 West Virginia         WV
49     Wisconsin         WI
50       Wyoming         WY

A lookup table/named vector has values as the vector, and keys as the names. So let me first make a vector of the values, which are in the PostalCode column:

getpostalcode <- postal_df$PostalCode

And next I add names from the State column.

names(getpostalcode) <- postal_df$State

To use this named vector as a lookup table, the format is mylookupvector['key'].

So here’s how to get the postal code for Arkansas:

getpostalcode['Arkansas'] 

If you want just the value, without the key, add the unname function to that value you get back:

unname(getpostalcode[‘Arkansas’])

Update: You can also get just one value using the format getpostalcode[['Arkansas']] -- that is, double brackets instead of adding unname(). Thanks to Peter Harrison for the tip via Twitter. However, Hadley Wickham notes that the double-bracket format only works for one value. If you are doing something like creating a new column in a data frame, stick to unname().

That’s all there is to it. I know this is a somewhat trivial example, but it has some real-world use. For example, I’ve got a named vector of FIPS codes that I need when working with US Census data.

I started with a data frame of states and FIPS codes called fipsdf (the code for that is below). Next, I created  a vector called getfips from the data frame's fips code column and added the states as names.

fipsdf <- rio::import("data/FIPS.csv")
getfips <- fipsdf$FIPS
names(getfips) <- fipsdf$State

Now if I want the FIPS code for Massachusetts, I can use getfips['Massachusetts'] . I would add unname() to get just the value without the name: unname(getfips['Massachusetts']) .

If having to keep using unname() gets too annoying, you can even make a little function from your lookup table:

get_state_fips <- function(state, lookupvector = getfips){
fipscode <- unname(lookupvector[state])
return(fipscode)
}

Here, I’ve got two arguments to my function. One is my “key,” in this case the state name; the other is lookupvector, which defaults to my getfips vector. 

And you can see how I use the function. It's just the function name with one argument, the state name: get_state_fips("New York") .

I can make a function that looks a bit more generic, such as

get_value <- function(mykey, mylookupvector){
myvalue <- mylookupvector[mykey]
myvalue <- unname(myvalue)
return(myvalue)
}

It has a more generic name for the function, get_value(); a more generic first argument name, mykey, and a second argument of mylookupvector that doesn’t default to anything.

It’s the same thing I’ve been doing all along: getting the value from the lookup vector with lookupvector['key'] and then running the unname() function. But it’s all wrapped inside a function. So, calling it is a bit more elegant.

I can use that function with any named vector I’ve created. Here, I’m using it with Arkansas and my getpostalcode vector: get_value("Arkansas", getpostalcode) .

Easy lookups in R! Just remember that names have to be unique. You can repeat values, but not keys.

I first saw this idea years ago in Hadley Wickham’s Advanced R book. I still use it a lot and hope you find it helpful, too.

Code to create data frame with postal abbreviations

postal_df <- data.frame(stringsAsFactors=FALSE,
State = c("Alabama", "Alaska", "Arizona", "Arkansas", "California",
"Colorado", "Connecticut", "Delaware", "Florida", "Georgia",
"Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas",
"Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts",
"Michigan", "Minnesota", "Mississippi", "Missouri", "Montana",
"Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico",
"New York", "North Carolina", "North Dakota", "Ohio",
"Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina",
"South Dakota", "Tennessee", "Texas", "Utah", "Vermont",
"Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"),
PostalCode = c("AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL", "GA",
"HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD",
"MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ",
"NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC", "SD",
"TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY")
)

Code to create data frame with FIPS codes

fipsdf <- data.frame(State = c("Alabama", "Alaska", "Arizona", "Arkansas", 
"California", "Colorado", "Connecticut", "Delaware", "Florida",
"Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa",
"Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts",
"Michigan", "Minnesota", "Mississippi", "Missouri", "Montana",
"Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico",
"New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma",
"Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota",
"Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington",
"West Virginia", "Wisconsin", "Wyoming"), FIPS = c("01", "02",
"04", "05", "06", "08", "09", "10", "12", "13", "15", "16", "17",
"18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28",
"29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
"40", "41", "42", "44", "45", "46", "47", "48", "49", "50", "51",
"53", "54", "55", "56"), stringsAsFactors = FALSE)