Sign up ×
Cross Validated is a question and answer site for people interested in statistics, machine learning, data analysis, data mining, and data visualization. It's 100% free, no registration required.

The answers to this question on SO returned a set of approximately 125 one- to two-letter names:

  [1] "Ad" "am" "ar" "as" "bc" "bd" "bp" "br" "BR" "bs" "by" "c"  "C" 
 [14] "cc" "cd" "ch" "ci" "CJ" "ck" "Cl" "cm" "cn" "cq" "cs" "Cs" "cv"
 [27] "d"  "D"  "dc" "dd" "de" "df" "dg" "dn" "do" "ds" "dt" "e"  "E" 
 [40] "el" "ES" "F"  "FF" "fn" "gc" "gl" "go" "H"  "Hi" "hm" "I"  "ic"
 [53] "id" "ID" "if" "IJ" "Im" "In" "ip" "is" "J"  "lh" "ll" "lm" "lo"
 [66] "Lo" "ls" "lu" "m"  "MH" "mn" "ms" "N"  "nc" "nd" "nn" "ns" "on"
 [79] "Op" "P"  "pa" "pf" "pi" "Pi" "pm" "pp" "ps" "pt" "q"  "qf" "qq"
 [92] "qr" "qt" "r"  "Re" "rf" "rk" "rl" "rm" "rt" "s"  "sc" "sd" "SJ"
[105] "sn" "sp" "ss" "t"  "T"  "te" "tr" "ts" "tt" "tz" "ug" "UG" "UN"
[118] "V"  "VA" "Vd" "vi" "Vo" "w"  "W"  "y"

And R import code:

nms <- c("Ad","am","ar","as","bc","bd","bp","br","BR","bs","by","c","C","cc","cd","ch","ci","CJ","ck","Cl","cm","cn","cq","cs","Cs","cv","d","D","dc","dd","de","df","dg","dn","do","ds","dt","e","E","el","ES","F","FF","fn","gc","gl","go","H","Hi","hm","I","ic","id","ID","if","IJ","Im","In","ip","is","J","lh","ll","lm","lo","Lo","ls","lu","m","MH","mn","ms","N","nc","nd","nn","ns","on","Op","P","pa","pf","pi","Pi","pm","pp","ps","pt","q","qf","qq","qr","qt","r","Re","rf","rk","rl","rm","rt","s","sc","sd","SJ","sn","sp","ss","t","T","te","tr","ts","tt","tz","ug","UG","UN","V","VA","Vd","vi","Vo","w","W","y")

Since the point of the question was to come up with a memorable list of object names to avoid, and most humans are not so good at making sense out of a solid block of text, I would like to visualize this.

Unfortunately I'm not exactly certain of the best way to do this. I had thought of something like a stem-and-leaf plot, only since there are no repeated values each "leaf" was placed in the appropriate column rather than being left justified. Or a wordcloud-style adaptation where letters are sized according to its prevalence.

How might this be most clearly and efficiently be visualized?

Visualizations which do either of the following fit in the spirit of this question:

  • Primary goal: Enhance the memorizability of the set of names by revealing patterns in the data

  • Alternate goal: Highlight interesting features of the set of names (e.g. which help visualize the distribution, most common letters, etc.)

Answers in R are preferred, but all interesting ideas are welcome.

Ignoring the single-letter names is allowed, since those are easier to just give as a separate list.

share|improve this question

5 Answers 5

up vote 11 down vote accepted

Here is a start: visualize these on a grid of first and second letters:

combi <- c("Ad", "am", "ar", "as", "bc", "bd", "bp", "br", "BR", "bs", 
"by", "c",  "C",  "cc", "cd", "ch", "ci", "CJ", "ck", "Cl", "cm", "cn", 
"cq", "cs", "Cs", "cv", "d",  "D",  "dc", "dd", "de", "df", "dg", "dn", 
"do", "ds", "dt", "e",  "E",  "el", "ES", "F",  "FF", "fn", "gc", "gl", 
"go", "H",  "Hi", "hm", "I",  "ic", "id", "ID", "if", "IJ", "Im", "In", 
"ip", "is", "J",  "lh", "ll", "lm", "lo", "Lo", "ls", "lu", "m",  "MH", 
"mn", "ms", "N",  "nc", "nd", "nn", "ns", "on", "Op", "P",  "pa", "pf", 
"pi", "Pi", "pm", "pp", "ps", "pt", "q",  "qf", "qq", "qr", "qt", "r",  
"Re", "rf", "rk", "rl", "rm", "rt", "s",  "sc", "sd", "SJ", "sn", "sp", 
"ss", "t",  "T",  "te", "tr", "ts", "tt", "tz", "ug", "UG", "UN", "V",  
"VA", "Vd", "vi", "Vo", "w",  "W",  "y")

df <- data.frame (first = factor (gsub ("^(.).", "\\1", combi), 
                                  levels = c (LETTERS, letters)),
                  second = factor (gsub ("^.", "", combi), 
                                  levels = c (LETTERS, letters)),
                  combi = combi))

ggplot (data = df, aes (x = first, y = second)) + 
   geom_text (aes (label = combi), size = 3) + 
   ## geom_point () +
   geom_vline (x = 26.5, col = "grey") + 
   geom_hline (y = 26.5, col = "grey")

(was: two letter) grid with letters

ggplot (data = df, aes (x = second)) + geom_histogram ()

second letter

ggplot (data = df, aes (x = first)) + geom_histogram ()

first letter

I gather:

  • of the one letter names,

    • fortunately i, j, k, and l are available (so I can index up to 4d arrays)
    • unfortunately t (time), c (concentration) are gone. So are m (mass), V (volume) and F (force). No radius r nor diameter d.
    • I can have pressure (p), amount of substance (n), and length l, though.
    • Maybe I'll have to change to greek names: ε is OK, but then shouldn't

      π <- pi


  • I can have whatever lowerUPPER name I want.

  • In general, starting with an upper case letter is a safer bet than lower case.

  • don't start with c or d

share|improve this answer
Nice start. Maybe add quadrant lines ( in a big + ) through the 2d plot to give a better sense of where the upper/lower case letters go? –  Ari B. Friedman Aug 8 '11 at 19:13
Thought I did that. Anyways, here it is. @gsk3: thanks for uploading the pictures! –  cbeleites Aug 8 '11 at 19:22
Nice. And on the contrary, thanks for providing an interesting answer to prompt #2. :-) –  Ari B. Friedman Aug 8 '11 at 19:25
Looking at your 2d plot, another suggestion might be to reduce it to a 27x26 grid and change symbols or colors (or jitter with alpha) if a given letter has lower/upper/both. Could also make the NA row a different color to separate it out visually. –  Ari B. Friedman Aug 8 '11 at 20:15
I did have a look at 27 x 26 before posting the answer (with color and shape according to first and second letter being upper case). But that didn't convey an easy message, so I immediately went back for the larger grid. –  cbeleites Aug 8 '11 at 20:54

Ok, here's my very quick take on a "periodic table"-like visualization, based on the SO question and the comments of the others. The main problem is the big difference in number of variables between packages, which kind of hinders the visualization... I realize this is very rough, so please feel free to change it as you wish.

Here is the current output (from my package list) Example plot

And the code

# Load all the installed packages
lapply(rownames(installed.packages()), require, 
       character.only = TRUE)
# Find variables of length 1 or 2
one_or_two <- unique(apropos("^[a-zA-Z]{1,2}$"))
# Find which package they come from
packages <- lapply(one_or_two, find)
# Some of the variables may belong to multiple packages, so determine the length 
# of each entry in packages and duplicate the names accordingly
lengths <- unlist(lapply(packages, length)) <- data.frame(var = rep(one_or_two, lengths), 
                   package = unlist(packages))

Now, we have a data frame like this:

> head(, 10)
   var           package
1   ar     package:stats
2   as   package:methods
3   BD    package:fields
4   bs      package:VGAM
5   bs   package:splines
6   by      package:base
7    c      package:base
8    C     package:stats
9   cm package:grDevices
10   D     package:stats

We can now split the data by package

 data.split <- split(,$package)

We can see that most variables come from the base and stats package

> unlist(lapply(data.split, nrow))
     package:base  package:datasets    package:fields 
               16                 1                 2 
  package:ggplot2 package:grDevices  package:gWidgets 
                2                 1                 1 
  package:lattice      package:MASS    package:Matrix 
                1                 1                 3 
  package:methods      package:mgcv      package:plyr 
                3                 2                 1 
     package:spam   package:splines     package:stats 
                1                 2                14 
 package:survival     package:utils      package:VGAM 
                1                 2                 4 

Finally, the drawing routine

plot(0, 0, "n", xlim=c(0, 100), ylim=c(0, 120), 
     xaxt="n", yaxt="n", xlab="", ylab="")

side.len.x <- 100 / length(data.split)
side.len.y <- 100 / max(unlist(lapply(data.split, nrow)))
colors <- rainbow(length(data.split), start=0.2, end=0.6)    

for (xcnt in 1:length(data.split))
    posx <- side.len.x * (xcnt-1)

    # Remove "package :" in front of the package name
    pkg <- unlist(strsplit(as.character(data.split[[xcnt]]$package[1]), ":"))
    pkg <- pkg[2]

    # Write the package name
    text(posx + side.len.x/2, 102, pkg, srt=90, cex=0.95, adj=c(0, 0))

    for (ycnt in 1:nrow(data.split[[xcnt]]))
        posy <- side.len.y * (ycnt-1)
        rect(posx, posy, posx+side.len.x*0.85, posy+side.len.y*0.9, col = colors[xcnt])
        text(posx+side.len.x/2, posy+side.len.y/2, data.split[[xcnt]]$var[ycnt], cex=0.7)
share|improve this answer
Nice! One interesting way to take this would be to group them by category (e.g. graphics packages, data manipulation practices, etc.), color code them, and then make the overall shape more box-like rather than histogram-like. –  Ari B. Friedman Aug 9 '11 at 7:36
+1 What a treat! :) Very nice work. I guess the only thing that would be necessary to get periodic table functionality is the table layout. The standard PT has 2 grids, with some elements missing in the top 1, and the groups are split/rearranged (as opposed to 1 group = 1 vertical column). To be honest, that's not the part that I thought would be hard. The coloring and block layout is the the part that excites me most & it's great to see ggplot2 code for it. –  Iterator Aug 9 '11 at 12:26
I need coffee. I see that gsk3 had the same comment with fewer words. :) I think I was mesmerized by color. –  Iterator Aug 9 '11 at 12:31
@Iterator: note that it's all R standard plot functions, no ggplot2 involved :) –  nico Aug 9 '11 at 13:08
Holy mackerel. You're right! Even more impressive. My conclusion: I neeeeeeed coffeeeeeeeeeee. –  Iterator Aug 9 '11 at 13:20

Here's a letter-based histogram. Considered sizing the first letters by number, but decided against since that's already encoded in the vertical component.

# "Load" data
nms <- c("Ad","am","ar","as","bc","bd","bp","br","BR","bs","by","c","C","cc","cd","ch","ci","CJ","ck","Cl","cm","cn","cq","cs","Cs","cv","d","D","dc","dd","de","df","dg","dn","do","ds","dt","e","E","el","ES","F","FF","fn","gc","gl","go","H","Hi","hm","I","ic","id","ID","if","IJ","Im","In","ip","is","J","lh","ll","lm","lo","Lo","ls","lu","m","MH","mn","ms","N","nc","nd","nn","ns","on","Op","P","pa","pf","pi","Pi","pm","pp","ps","pt","q","qf","qq","qr","qt","r","Re","rf","rk","rl","rm","rt","s","sc","sd","SJ","sn","sp","ss","t","T","te","tr","ts","tt","tz","ug","UG","UN","V","VA","Vd","vi","Vo","w","W","y") #all names
two_in_base <- c("ar", "as", "by", "cm", "de", "df", "dt", "el", "gc", "gl", "if", "Im", "is", "lh", "lm", "ls", "pf", "pi", "pt", "qf", "qr", "qt", "Re", "rf", "rm", "rt", "sd", "ts", "vi") # 2-letter names in base R
vowels <- c("a","e","i","o","u")
vowels <- c( vowels, toupper(vowels) )

# Constants <- 3

# Define a function to give us consistent X coordinates
returnX <- function(vec) {
  sapply(vec, function(x) seq(length(all.letters))[ x == all.letters ] )

# Make df of 2-letter names
combi <- nms[ sapply( nms, function(x) nchar(x)==2 ) ]
combidf <- data.frame( first = substr(combi,1,1), second=substr(combi,2,2) )
combidf <- arrange(combidf,first,second)

# Add vowels
combidf$first.vwl <- (combidf$first %in% vowels)
combidf$second.vwl <- (combidf$second %in% vowels)

# Flag items only in base R
combidf$in_base <- paste(combidf$first,combidf$second,sep="") %in% two_in_base

# Create a data.frame to hold our plotting information for the first letters
combilist <- dlply(combidf,.(first),function(x) x$second)
combi.first <- data.frame( first = names(combilist), n = sapply(combilist,length) ,stringsAsFactors=FALSE )
combi.first$y <- 0
all.letters <-  c(letters,LETTERS) # arrange(combi.first,desc(n))$first to go in order of prevalence (which may break the one-letter name display)
combi.first$x <- returnX( combi.first$first )

# Create a data.frame to hold plotting information for the second letters
combidf$x <- returnX( combidf$first )
combidf$y <- unlist( by( combidf$second, combidf$first, seq_along ) )

# Make df of 1-letter names
sngldf <- data.frame( sngl = nms[ sapply( nms, function(x) nchar(x)==1 ) ] )
singles.y <- max(combidf$y) +
sngldf$y <- singles.y
sngldf$x <- returnX( sngldf$sngl )

# Plot
ggplot(data=combidf, aes(x=x,y=y) ) +
  geom_text(aes( label=second, size=3, colour=combidf$in_base ), position=position_jitter(w=0,h=.25)) +
  geom_text( data=combi.first, aes( label=first, x=x, y=y, size=4 ) ) +
  geom_text( data=sngldf, aes( label=sngl, x=x, y=y, size=4 ) ) +
  scale_size(name="Order (2-letter names)",limits=c(1,4),breaks=c(1,2),labels=c("Second","First")) +
  scale_x_continuous("",breaks=c(13,39),labels=c("lower","UPPER")) +
  scale_y_continuous("",breaks=c(0,5,singles.y),labels=c("First letter of two-letter names","Second letter of two-letter names","One-letter names") ) +
  coord_equal(1.5) +
  labs( colour="In base R" )

version with one- and two-letter names on same plot

letter-based histogram

share|improve this answer

Periodic Table for 100, Alex. I don't have code for it, though. :(

One might think that a "periodic table" package might already exist in CRAN. The idea of a coloring scheme and layout of such data could be interesting and useful.

These could be colored by package and sorted vertically by frequency, e.g. in a sample of code on CRAN or as they appear in one's local codebase.

share|improve this answer
Not sure if I follow you... could you make a simple sketch of what you are thinking of? I don't see how a periodic table layout would help here... –  nico Aug 8 '11 at 19:36
@nico: I am thinking of something like this: Suppose that we replace "nobel elements" with the base R commands. The halogens might be replaced by one's own package(s), and so on. With such a visualization package, I would leave it to the user to specify the nature of rows, columns, groups, and colorings. It should be a fairly simple thing to implement, though I would do it very crudely. Placement would be such that items in the same group (i.e. package) are near each other. Vertical placement could be determined by usage frequency. –  Iterator Aug 8 '11 at 19:41
OK now I understand! Maybe I'll try to see if I can come out with something but I need to find some spare time first... :( –  nico Aug 8 '11 at 20:09
I don't quite see it yet, but I'm excited to see what this idea turns into :-) –  Ari B. Friedman Aug 8 '11 at 20:13
had a look at stackexchange: Tal Galili did ask about PSE a while ago, so I didn't ask. But I just pushed a first bit of code to r-forge: pse.R please put stars around the checkout - I don't know how to escape them so they vanish... –  cbeleites Aug 8 '11 at 22:37

The first two pages in chapter 2 of MacKay's ITILA has nice diagrams showing the conditional probabilities of all character pairings in the English language. You may find it of use.

I'm embarrassed to say that I don't remember what program was used to produce them.

share|improve this answer
It's cool, but it looks to me like those all depend on having some additional information (prevalence) associated with each letter-letter pair. Thus he's graphing 3 dimensions whereas we're mainly graphing 2.... I'd love to have the prevalence info for R, though. But that's a data mining operation for another day. –  Ari B. Friedman Aug 9 '11 at 13:11

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.