Lists

Lists are a collections of objects. They can contain anything, including other lists.

Here we cover:

Lists: Code

Create a list using list(). Use the str function to display the list in a nice format.

x <- list(4:9, "test", FALSE, list(3,"BALL"))
is.list(x)
## [1] TRUE
x
## [[1]]
## [1] 4 5 6 7 8 9
## 
## [[2]]
## [1] "test"
## 
## [[3]]
## [1] FALSE
## 
## [[4]]
## [[4]][[1]]
## [1] 3
## 
## [[4]][[2]]
## [1] "BALL"
str(x)
## List of 4
##  $ : int [1:6] 4 5 6 7 8 9
##  $ : chr "test"
##  $ : logi FALSE
##  $ :List of 2
##   ..$ : num 3
##   ..$ : chr "BALL"

We can give elements of the list names (you can do the same with vectors):

y <- list(nums = 1:4, letters = letters[1:4])
str(y)
## List of 2
##  $ nums   : int [1:4] 1 2 3 4
##  $ letters: chr [1:4] "a" "b" "c" "d"

Extract single elements from a list using double square brackets:

y[[1]] #seldom used
## [1] 1 2 3 4
y[["nums"]]
## [1] 1 2 3 4
y$nums #shorthand
## [1] 1 2 3 4

Add an element to a list

y$roman <- c("I","II","III","IV")
y
## $nums
## [1] 1 2 3 4
## 
## $letters
## [1] "a" "b" "c" "d"
## 
## $roman
## [1] "I"   "II"  "III" "IV"

single brackets can be used to extract multiple elements from a list; it returns a list!

y[c("nums","roman")]
## $nums
## [1] 1 2 3 4
## 
## $roman
## [1] "I"   "II"  "III" "IV"
y[c("nums")] #returns a list of length one
## $nums
## [1] 1 2 3 4
y[[c("nums")]] # returns a vector
## [1] 1 2 3 4

You can access/set the names of list elements using the names function:

names(y)
## [1] "nums"    "letters" "roman"
names(y) <- c("Nums","Letters","Roman")
y
## $Nums
## [1] 1 2 3 4
## 
## $Letters
## [1] "a" "b" "c" "d"
## 
## $Roman
## [1] "I"   "II"  "III" "IV"

You can delete from lists by setting a given element to NULL

y$Roman <- NULL

Preview of next session: functions to fit linear models return lists

reg <- lm(GNP ~ Population + Employed, data = longley)
sum <- summary(reg)
str(sum, give.attr=F)
## List of 11
##  $ call         : language lm(formula = GNP ~ Population +      Employed, data = longley)
##  $ terms        :Classes 'terms', 'formula' length 3 GNP ~ Population + Employed
##  $ residuals    : Named num [1:16] -11.689 -4.551 -4.691 0.218 11.274 ...
##  $ coefficients : num [1:3, 1:4] -1372.095 8.556 11.561 36.141 0.984 ...
##  $ aliased      : Named logi [1:3] FALSE FALSE FALSE
##  $ sigma        : num 7.39
##  $ df           : int [1:3] 3 13 3
##  $ r.squared    : num 0.995
##  $ adj.r.squared: num 0.994
##  $ fstatistic   : Named num [1:3] 1352 2 13
##  $ cov.unscaled : num [1:3, 1:3] 23.9488 0.1211 -0.5834 0.1211 0.0177 ...
sum$coefficients
##                 Estimate Std. Error
## (Intercept) -1372.095356  36.140646
## Population      8.556108   0.983721
## Employed       11.560564   1.948441
##                t value     Pr(>|t|)
## (Intercept) -37.965435 1.050873e-14
## Population    8.697698 8.849592e-07
## Employed      5.933238 4.958268e-05

Data Frames

Data frames are the data type most like Stata’s dataset. Remember, matrices can only store elements of one type, so they’re not suitable for storing data where some columns are numbers, some strings, etc.

Data frames are actually lists of equal length vectors.

Data Frames: Code

Create a data frame out of vectors using data.frame

nums <- data.frame(numerals=1:10,
                   roman = as.character(as.roman(1:10)), 
                   letters = letters[1:10],
                   stringsAsFactors = F)

So what we know about accessing elements of lists applies to dataframes:

nums[["numerals"]]  #extract a vector
##  [1]  1  2  3  4  5  6  7  8  9 10
nums$numerals #equivalent
##  [1]  1  2  3  4  5  6  7  8  9 10
nums["numerals"] #extract a dataframe (one column)
##    numerals
## 1         1
## 2         2
## 3         3
## 4         4
## 5         5
## 6         6
## 7         7
## 8         8
## 9         9
## 10       10
nums[c("numerals","roman")] #extract a data frame (two columns)
##    numerals roman
## 1         1     I
## 2         2    II
## 3         3   III
## 4         4    IV
## 5         5     V
## 6         6    VI
## 7         7   VII
## 8         8  VIII
## 9         9    IX
## 10       10     X

We can use what we learnt about matrix subsetting on data frames too:

nums[1:3,] #first three rows, all columns
##   numerals roman letters
## 1        1     I       a
## 2        2    II       b
## 3        3   III       c
nums[4,3]  #fourth row, third column
## [1] "d"
nums[, 1:2] #all rows, first and second columns. Shorthand for this: nums[1:2]
##    numerals roman
## 1         1     I
## 2         2    II
## 3         3   III
## 4         4    IV
## 5         5     V
## 6         6    VI
## 7         7   VII
## 8         8  VIII
## 9         9    IX
## 10       10     X
nums$numerals > 5
##  [1] FALSE FALSE FALSE FALSE FALSE  TRUE
##  [7]  TRUE  TRUE  TRUE  TRUE
nums[nums$numerals > 5, ]
##    numerals roman letters
## 6         6    VI       f
## 7         7   VII       g
## 8         8  VIII       h
## 9         9    IX       i
## 10       10     X       j
#install.packages("stringr"")
library(stringr)
nums[nums$numerals > 5 & str_detect(nums$roman,"V"),]
##   numerals roman letters
## 6        6    VI       f
## 7        7   VII       g
## 8        8  VIII       h

And use subsetting to replace values

nums[1:3,]$numerals <- -1
nums
##    numerals roman letters
## 1        -1     I       a
## 2        -1    II       b
## 3        -1   III       c
## 4         4    IV       d
## 5         5     V       e
## 6         6    VI       f
## 7         7   VII       g
## 8         8  VIII       h
## 9         9    IX       i
## 10       10     X       j
nums[str_detect(nums$roman,"V"), ]$roman <- "v"
nums
##    numerals roman letters
## 1        -1     I       a
## 2        -1    II       b
## 3        -1   III       c
## 4         4     v       d
## 5         5     v       e
## 6         6     v       f
## 7         7     v       g
## 8         8     v       h
## 9         9    IX       i
## 10       10     X       j
#alternative method using ifelse
nums$roman <- ifelse(nums$roman == "v", "u", nums$roman)
nums
##    numerals roman letters
## 1        -1     I       a
## 2        -1    II       b
## 3        -1   III       c
## 4         4     u       d
## 5         5     u       e
## 6         6     u       f
## 7         7     u       g
## 8         8     u       h
## 9         9    IX       i
## 10       10     X       j

Add columns to a data frame:

nums$LETTERS <- LETTERS[1:10]
nums
##    numerals roman letters LETTERS
## 1        -1     I       a       A
## 2        -1    II       b       B
## 3        -1   III       c       C
## 4         4     u       d       D
## 5         5     u       e       E
## 6         6     u       f       F
## 7         7     u       g       G
## 8         8     u       h       H
## 9         9    IX       i       I
## 10       10     X       j       J

or create a new dataframe:

cbind(nums, letters_reversed = LETTERS[10:1])
##    numerals roman letters LETTERS
## 1        -1     I       a       A
## 2        -1    II       b       B
## 3        -1   III       c       C
## 4         4     u       d       D
## 5         5     u       e       E
## 6         6     u       f       F
## 7         7     u       g       G
## 8         8     u       h       H
## 9         9    IX       i       I
## 10       10     X       j       J
##    letters_reversed
## 1                 J
## 2                 I
## 3                 H
## 4                 G
## 5                 F
## 6                 E
## 7                 D
## 8                 C
## 9                 B
## 10                A

Add rows together (‘append’ in Stata) using rbind:

rbind(nums[nums$numerals > 8, ], nums[nums$numerals < 3, ])
##    numerals roman letters LETTERS
## 9         9    IX       i       I
## 10       10     X       j       J
## 3        -1     I       a       A
## 4        -1    II       b       B
## 5        -1   III       c       C

Access column names using names:

names(nums)
## [1] "numerals" "roman"    "letters" 
## [4] "LETTERS"
names(nums)[1] <- "Numerals" #fragile!

Delete a column by setting it to NULL:

nums$letters <- NULL
nums
##    Numerals roman LETTERS
## 1        -1     I       A
## 2        -1    II       B
## 3        -1   III       C
## 4         4     u       D
## 5         5     u       E
## 6         6     u       F
## 7         7     u       G
## 8         8     u       H
## 9         9    IX       I
## 10       10     X       J

Data Frames: Exercices

Load a dataframe containing data on some cars using

  cars <- as.data.frame(
    read.table("http://goo.gl/TQCljj",
               header=T, stringsAsFactors = F)) 
  1. Replace the ‘wt’ column, which contains each vehicles’ weight in pounds, with weight in kilograms (multiply by 0.45)

  2. Extract a dataframe consisting of all automatic cars (i.e. those with a 1 in the ‘am’) column

  3. Set the ‘gear’ column to -1 for automatic vehicles

  4. Without just reading off the table: how many cars have got more than six cylinders (‘cyl’)? Get a vector of their model names. What is their combined horsepower (‘hp’)?

  5. Optional: Figure out how to use the setdiff() function to select all columns apart from the ‘disp’ column.

More Matrices: apply function

The *apply functions are a really handy group of functions. What in other language you might achieve with a loop, you will normally write with an apply function (loops also exist in R, but are generally frowned upon, for a discuss of when they do make sense, see http://adv-r.had.co.nz/Functionals.html).

The apply function allows you to perform a function on every row, column, or element of a matrix.

For example, let’s find the maximum number in a matrix by row and by column:

z <- rbind(c(4,7,6,1),c(0,9,0,2),c(5,3,0,7))
z
##      [,1] [,2] [,3] [,4]
## [1,]    4    7    6    1
## [2,]    0    9    0    2
## [3,]    5    3    0    7
apply(z, MARGIN = 1, FUN = max) #max by row - the first margin
## [1] 7 9 7
apply(z, MARGIN = 2, FUN = max) #max by column - second margin
## [1] 5 9 6 7

You can define your own function, and pass that as an argument to apply(). Here’s a function that binarises a variable to 0/1 depending on whether it is greater than or less than zero.

binarize <- function(x) {
  if(x > 0)
    1
  else
    0
}

apply(z, MARGIN = c(1,2), FUN=binarize)
##      [,1] [,2] [,3] [,4]
## [1,]    1    1    1    1
## [2,]    0    1    0    1
## [3,]    1    1    0    1

You can also use functions with more arguments, as this shows:

binarize <- function(x,threshold) {
  if(x > threshold)
    1
  else
    0
}

apply(z, MARGIN = c(1,2), FUN=binarize, threshold=3)
##      [,1] [,2] [,3] [,4]
## [1,]    1    1    1    0
## [2,]    0    1    0    0
## [3,]    1    0    0    1

More Matrices: Exercises

Exercise 1

Construct the following matrix in R, and then use the apply function to find the square roots of its row-wise sums:

\[ \begin{matrix} 1 & 5 & 9 & 13 \\ 2 & 6 & 10 & 14 \\ 3 & 7 & 11 & 15 \\ 4 & 8 & 12 & 16 \end{matrix} \]

Hint: look back at Session 1 materials on matrix construction to create the matrix! Then write a function standardise which you should be able to use like this:

standardise(c(1,5,9,13))
## [1] 0.1428571 0.7142857 1.2857143
## [4] 1.8571429

Exercise 2 (a bit harder!)

Write a function to find the most ‘deviant’ entry in each row of a matrix. The most deviant observation is that furthest from the median. The function should be called most_deviant and take a single argument, which will be a matrix. Test it using the z matrix we created earlier:

 most_deviant(z)
## [1] 1 9 0

Your function should be defined like this:

most_deviant <- function(x) {
  #All logic here. 
}

and make use of apply. You will probably find the which.max function useful. Hint: the pattern to the solution is identical to the question above.

lapply and sapply: Code

These are really useful functions for carrying out an operation on every element of a list or a vector. Remember that data frames are actually a type of list, so if you want to do something on one or more columns, using these functions are normally the way to do it.

We’ll start by creating a list to play with.

l <- list()
l$a <- 1
l$b <- 2
l$c <- 3

l
## $a
## [1] 1
## 
## $b
## [1] 2
## 
## $c
## [1] 3

‘lapply’ stands for list apply. It takes every item in a list/vector and applies a given operation on it.

lapply(l, sin)
## $a
## [1] 0.841471
## 
## $b
## [1] 0.9092974
## 
## $c
## [1] 0.14112
lapply(l, function(x) { x+1 })
## $a
## [1] 2
## 
## $b
## [1] 3
## 
## $c
## [1] 4
t <- list(1:7 , 5:20, 3:14)
t
## [[1]]
## [1] 1 2 3 4 5 6 7
## 
## [[2]]
##  [1]  5  6  7  8  9 10 11 12 13 14 15 16
## [13] 17 18 19 20
## 
## [[3]]
##  [1]  3  4  5  6  7  8  9 10 11 12 13 14
lapply(t,mean)
## [[1]]
## [1] 4
## 
## [[2]]
## [1] 12.5
## 
## [[3]]
## [1] 8.5
lapply(t,max)
## [[1]]
## [1] 7
## 
## [[2]]
## [1] 20
## 
## [[3]]
## [1] 14
t <- list(23,6,4,9)
lapply(t,sample)
## [[1]]
##  [1] 10  4 16 13  6 19  8  7 15 22 11 23
## [13] 20 17  3  9 18  2 21  5  1 12 14
## 
## [[2]]
## [1] 5 6 1 4 3 2
## 
## [[3]]
## [1] 1 4 3 2
## 
## [[4]]
## [1] 2 5 7 3 1 6 8 4 9
#same thing but with vectors:

t <- c(23,6,4,9)
lapply(t,sample)
## [[1]]
##  [1]  4  5 11  2 17 18 15 22 20  6 13 14
## [13] 12  3  8  9 19 16 21 10  7  1 23
## 
## [[2]]
## [1] 2 1 3 4 5 6
## 
## [[3]]
## [1] 4 1 2 3
## 
## [[4]]
## [1] 7 8 2 1 6 5 9 3 4

sapply is a convenient wrapper around lapply which ‘simplifies’ the output from a list into a data frame (or vector, if only a single column). Let’s extract all the numeric columns from the cars dataset.

lapply(cars, is.numeric)
## $model
## [1] FALSE
## 
## $mpg
## [1] TRUE
## 
## $cyl
## [1] TRUE
## 
## $disp
## [1] TRUE
## 
## $hp
## [1] TRUE
## 
## $drat
## [1] TRUE
## 
## $wt
## [1] TRUE
## 
## $am
## [1] TRUE
## 
## $gear
## [1] TRUE
sapply(cars, is.numeric) 
## model   mpg   cyl  disp    hp  drat 
## FALSE  TRUE  TRUE  TRUE  TRUE  TRUE 
##    wt    am  gear 
##  TRUE  TRUE  TRUE
numeric_columns <- sapply(cars, is.numeric)

lapply and sapply: Exercise

General advice: build up to the solutions one step at a time!

  1. Use lapply to censor each vector in the list a: replace any values greater than 10 with 10. You should create a with:
t <- list(23,6,4,9)
a <- lapply(t,sample)
  1. ‘standardise’ each vector in the list a. You standardise a vector by dividing each element by the mean of all elements.

  2. Use sapply to find out which model ‘wins’ each category in cars (e.g. most mpg, most cyl, most carb etc). It doesn’t matter which gets picked if there is a tie. You’ll need to deal with the problem that one column contains a string, of which there is no natural ranking. The output should look something like this:

##                   mpg 
##      "Toyota Corolla" 
##                   cyl 
##   "Hornet Sportabout" 
##                  disp 
##  "Cadillac Fleetwood" 
##                    hp 
##       "Maserati Bora" 
##                  drat 
##         "Honda Civic" 
##                    wt 
## "Lincoln Continental" 
##                    am 
##           "Mazda RX4" 
##                  gear 
##       "Porsche 914-2"

Hint – you will probably find the which.max() function useful.

  1. Run the following code.
source(
  "http://markwestcott34.github.io/economics/teaching/ss2015/R/code/numbers2words.r"
  )

d <- as.data.frame(lapply(1:100, sample, 50,replace=T))
names(d) <- numbers2words(1:100)

The first command will download a function called numbers2words from my website. The numbers2words function takes a number and returns that same number in words.

  1. Make sure you understand how the construction of the d dataframe works (run parts of each line at a time, examine output, etc.)

  2. Make a copy of d. Extract any column from the list which has got the word ‘two’ in its name.

Hint: you can use the str_detect from the stringr package to test if one string contains another. Install the package with install.packages("stringr") and use it like this:

library(stringr)
str_detect("I wonder if 'apple' is in this string", "apple")
## [1] TRUE
  1. Now delete the columns from your copy of d which have ‘two’ in their name. You can delete multiple items from a list as follows:
nums[1:2] <- list(NULL)
  1. (hard!) Make your answer to b. more general. Write a function that takes a list and a vector of strings. Return all the elements in the list whose names dont’ match any of the strings. You should be able to use it like this:
names(removeMatchingColumns(d, c("one","two","three")))
##  [1] "four"          "five"         
##  [3] "six"           "seven"        
##  [5] "eight"         "nine"         
##  [7] "ten"           "eleven"       
##  [9] "twelve"        "thirteen"     
## [11] "fourteen"      "fifteen"      
## [13] "sixteen"       " seventeen"   
## [15] "eighteen"      "nineteen"     
## [17] "twenty"        "twenty four"  
## [19] "twenty five"   "twenty six"   
## [21] "twenty seven"  "twenty eight" 
## [23] "twenty nine"   "thirty"       
## [25] "thirty four"   "thirty five"  
## [27] "thirty six"    "thirty seven" 
## [29] "thirty eight"  "thirty nine"  
## [31] "forty"         "forty four"   
## [33] "forty five"    "forty six"    
## [35] "forty seven"   "forty eight"  
## [37] "forty nine"    "fifty"        
## [39] "fifty four"    "fifty five"   
## [41] "fifty six"     "fifty seven"  
## [43] "fifty eight"   "fifty nine"   
## [45] "sixty"         "sixty four"   
## [47] "sixty five"    "sixty six"    
## [49] "sixty seven"   "sixty eight"  
## [51] "sixty nine"    "seventy"      
## [53] "seventy four"  "seventy five" 
## [55] "seventy six"   "seventy seven"
## [57] "seventy eight" "seventy nine" 
## [59] "eighty"        "eighty four"  
## [61] "eighty five"   "eighty six"   
## [63] "eighty seven"  "eighty eight" 
## [65] "eighty nine"   "ninety"       
## [67] "ninety four"   "ninety five"  
## [69] "ninety six"    "ninety seven" 
## [71] "ninety eight"  "ninety nine"

This is how you delete multiple elements from a list:

nums[1:2] <- list(NULL)
  1. Figure out how this line of code works:
manufacturer <- sapply(strsplit(cars[,"model"],split=" "),"[",1) 

Reading external data and factors

Download the ‘iris.csv’ dataset from http://markwestcott34.github.io/economics/teaching/ss2015/R/datasets/iris.csv and store it somewhere on your PC.

Use the setwd() function to set the working directory to the folder that contains the file, e.g.

setwd("/Users/mark/tresor/Economics/Teaching/R/materials/session2")

You can also do this using the ‘Files’ tab in the bottom right of RStudio (click More and then Set as Working Directory). Note the need for forward slashes or double backslashes:

setwd("C:\\Users\\Mark\\Documents and Settings\\R\\") #correct 
setwd("C:/Users/Mark/Documents and Settings/R/") #correct 
setwd("C:\Users\Mark\Documents and Settings\R\") #wrong 
"

So you can read in a CSV file with read_csv from the readr package:

#install.packages("readr")
library(readr)
setwd("/Users/mark/tresor/Economics/Teaching/R/materials/session2")
iris <- read_csv("iris.csv")

I recommend this approach over the read.csv method from base R:

read.csv(file, header = TRUE, sep = ",", quote = "\"",
         dec = ".", fill = TRUE, comment.char = "", ...)

Here, you normally need to remember to pass the stringsAsFactors=FALSE argument. By doing so, we are telling R not to convert strings to factors. Factors are like labelled variables in Stata and they are often (but not always..) a pain.

setwd("/Users/mark/tresor/Economics/Teaching/R/materials/session2")

iris <- read.csv("iris.csv")
class(iris$species)
## [1] "factor"
head(iris$species , n = 20)
##  [1] setosa setosa setosa setosa setosa
##  [6] setosa setosa setosa setosa setosa
## [11] setosa setosa setosa setosa setosa
## [16] setosa setosa setosa setosa setosa
## 3 Levels: setosa ... virginica
levels(iris$species)
## [1] "setosa"     "versicolor"
## [3] "virginica"
iris <- read.csv("iris.csv", stringsAsFactors=F)
class(iris$species)
## [1] "character"
head(iris$species)
## [1] "setosa" "setosa" "setosa" "setosa"
## [5] "setosa" "setosa"

You can read Stata files in using read_stata from the haven package, which also provides functions to read SPSS/SAS.

For reading Excel files use the read_excel function from the readxl package.

Debugging

Debugging is the art of finding and squashing bugs in your code. This is somewhere I think Stata is really weak. The debugging features in R allow you to ‘step through’ code one line at a time, set ‘breakpoints’, and examine the contents of variables.

rm(list = ls())


sqrtPlusFour <- function(z) {
  y <- z + 4
  sqrt(y)
}

doSomething <- function(a) {
  if (a == 3) {
    print("a is 3")
  } else {
    sqrtPlusFour(a)
  }
}


#sqrtPlusFour("hi")

RStudio automatically shows the ‘call stack’ in the Traceback window. Copy the following code into a new file, save and ‘Source’ it:

f <- function(x) { 
  g(x)
}

g <- function(y) {
  h(y) 
}

h <- function(z) {
  "a" - z
}

f(10)

Debugging: Exercises

Exercise 1

Here is a buggy function, findruns. The aim of the function is to find all k-length sequences of 1s in a vector, e.g:

findruns( c(1,1), k = 2)
## [1] 1
findruns( c(1,1,0,1,1), k = 2)
## [1] 1 4
findruns( c(0,1,1,0,0), k = 2)
## [1] 2
findruns( c(1,1,0,0,0,0,1,1,1), k = 2)
## [1] 1 7 8
findruns( c(1,1,0,0,0,0,1,1,1), k = 3)
## [1] 7

Here’s some buggy code for the function:

findruns <- function(x,k) {
   n <- length(x)
   runs <- NULL
   for (i in 1:(n-k)) {
     if (all(x[i:i+k-1]==1))
       runs <- c(runs,i)
   }
   return(runs) 
}

As you’ll see, the function doesn’t work correctly:

findruns(c(1,0,0,1,1,0,1,1,1),2) #should return 4,7,8
## [1] 3 4 6 7

Use the debugger to find and fix the problem(s) in the code.

Exercise 2

This exercise is based on one in Matloff (2009).

The mind function is designed to efficiently find the lowest entry in a distance matrix. A distance matrix is a symmetric matrix, where q[i,j] represents the absolute distance between cities i and j.

\[ \begin{pmatrix} & London & Munich & Montreal & Frankfurt \\ London & 0 & 916 & 5219 & 636 \\ Munich & 916 & 0 & 6133 & 303 \\ Montreal & 5219 & 6133 & 0 & 5841 \\ Frankfurt & 636 & 303 & 5841 & 0 \end{pmatrix} \]

mind is a function which, given a distance matrix, should find the two cities which are closest to each other and return a vector of three elements: that minimum distance, and the ids of those cities. For example,

q <- rbind(c(0,12,13,8,20),
           c(12,0,15,28,88),
           c(13,15,0,6,9),
           c(8,28,6,0,33),
           c(20,88,9,33,0))
q
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    0   12   13    8   20
## [2,]   12    0   15   28   88
## [3,]   13   15    0    6    9
## [4,]    8   28    6    0   33
## [5,]   20   88    9   33    0
mind(q)
## [1] 6 3 4

Here is some code for the mind() function. Use the debugger to figure out how it is meant to work, and fix the bugs! When working out how the function should work, remember that the distance matrix is symmetrical.

imin <- function(x) {
  lx <- length(x)
  
  i <- x[lx] #number of row from original matrix
  
  # find the minimum entry in the part of the row corresponding to the upper-right triangle 
  j <- which.min(x[(i+1):(lx-1)])

  #return the position of the entry and the value
  return( c(j, x[j]) )
}

mind <- function(d) {
   
   #make sure we are being passed a symetric matrix
   stopifnot(isSymmetric.matrix(d))
   n <- nrow(d)
   
   #add a column to the matrix which identifies the row number
   dd <- cbind(d,1:n)
   
   #find the minimum value and position of that value in each row (apart from the last)
   wmins <- apply(dd[-n,],1,imin)
   
   #wmins now contains two rows
   #row 1 are indices to the lowest value of each original row
   #row 2 the actual value
   
   #now find which original row contained the overall lowest number
   i <- which.min(wmins[1,])
   
   #and the lowest number in that row
   j <- wmins[2,i]
   
   #return the overall minimum value and the position in the matrix  
   return(c(d[i,j],i,j))
 }

mind(q)
## Error in d[i, j]: subscript out of bounds