Friday, November 1, 2013

Session 6 Updates

Hi all,

Session 6 is done. We covered two main ways to map perceptual data - (i) using the attribute ratings (AR) method to create p-maps and joint-space maps (JSMs), and (ii) using the overall similarity (OS) approach to create multidimensional scaling (MDS) maps.We also saw some 101 stuff on positioning, definitional terms, common positioning startegies etc. The point was to get you thinking on how the mapping process could throw insights onto positioning in general, which strategy to adopt based on what criteria etc.

OK, next, what will follow is the code and snapshots of the plots that emerge from the classwork examples I did. Again, you are strongly encouraged to replicate the classwork examples at home. Copy-paste a only a few lines of code at a time after reading the comments next to each line of code. {P.S.- the statements following a '#' are for documentation purposes only and aren't executed}.So, without further ado, let us start right away:

##########################################

1. Simple Data Visualization using biplots: USArrests example.

We use USArrests data (inbuilt R dataset) to see how it can be visualized in 2 dimensions. Just copy-paste the code below onto the R console [Hit 'enter' after the last line]. Need to install package "MASS". Don't reinstall if you have already installed it previously. A package once installed lasts forever.

rm(list = ls()) # clear workspace

install.packages("MASS") # install MASS package

mydata = USArrests # USArrests is an inbuilt dataset

pc.cr = princomp(mydata, cor=TRUE) # princomp() is core func summary(pc.cr) # summarize the pc.cr object

biplot(pc.cr) # plot the pc.cr object

abline(h=0); abline(v=0) # draw horiz and vertical axes

This is what the plot should look like. Click on image for larger view.

2. Code for making Joint Space maps:

I have coded a user-defined function called JSM in R. You can use it whenever you need to make joint space maps provided just by invoking the function. All it requires to work is a perceptions table and a preference rating table. First copy-paste the entire block of code below onto your R console. Those interested in reading the code, pls copy-paste line-by-line. I have put explanations in comments ('#') for what the code is doing.

## --- Build func to run simple perceptual maps --- ##

JSM = function(inp1, prefs){ #JSM() func opens

# inp1 = perception matrix with row and column headers
# brands in rows and attributes in columns
# prefs = preferences matrix

par(pty="s") # set square plotting region

fit = prcomp(inp1, scale.=TRUE) # extract prin compts

plot(fit$rotation[,1:2], # use only top 2 prinComps

type ="n", xlim=c(-1.5,1.5), ylim=c(-1.5,1.5), # plot parms

main ="Joint Space map - Home-brew on R") # plot title

abline(h=0); abline(v=0) # build horiz and vert axes

attribnames = colnames(inp1);

brdnames = rownames(inp1)

# -- insert attrib vectors as arrows --

for (i1 in 1:nrow(fit$rotation)){

arrows(0,0, x1 = fit$rotation[i1,1]*fit$sdev[1],

y1 = fit$rotation[i1,2]*fit$sdev[2], col="blue", lwd=1.5);

text(x = fit$rotation[i1,1]*fit$sdev[1], y = fit$rotation[i1,2]*fit$sdev[2],

labels = attribnames[i1],col="blue", cex=1.1)}

# --- make co-ords within (-1,1) frame --- #

fit1=fitfit1$x[,1]=fit$x[,1]/apply(abs(fit$x),2,sum)[1]

fit1$x[,2]=fit$x[,2]/apply(abs(fit$x),2,sum)[2]

points(x=fit1$x[,1], y=fit1$x[,2], pch=19, col="red")

text(x=fit1$x[,1], y=fit1$x[,2], labels=brdnames, col="black", cex=1.1)

# --- add preferences to map ---#

k1 = 2; #scale-down factor

pref = data.matrix(prefs)# make data compatible

pref1 = pref %*% fit1$x[,1:2]for (i1 in 1:nrow(pref1)){

segments(0, 0, x1 = pref1[i1,1]/k1, y1 = pref1[i1,2]/k1, col="maroon2", lwd=1.25)

points(x = pref1[i1,1]/k1, y = pref1[i1,2]/k1, pch=19, col="maroon2")

text(x = pref1[i1,1]/k1, y = pref1[i1,2]/k1, labels = rownames(pref)[i1], adj = c(0.5, 0.5), col ="maroon2", cex = 1.1)}

# voila, we're done! #} # JSM() func ends

3. OfficeStar MEXL example done on R

Goto LMS folder 'Session 6 files'. The file 'R code officestar.txt' contains the code (which I've broken up into chunks and annotated below) and the files 'officestar data1.txt' and 'officestar pref data2.txt' contain the average perceptions or attribute table and preferences table respectively.

Step 3a: Read in the attribute table into 'mydata'.

# -- Read in Average Perceptions table -- #

mydata = read.table(file.choose(), header = TRUE)

mydata = t(mydata) #transposing to ease analysis

mydata #view the table read

# extract brand and attribute names #

brdnames = rownames(mydata);

attribnames = colnames(mydata)

Step 3b: Read into R the preferences table into 'prefs'.

# -- Read in preferences table -- #

pref = read.table(file.choose())

dim(pref) #check table dimensions

pref[1:10,] #view first 10 rows

Data reading is done. You should see the data read-in as in the figure above. We can start analysis now. Finally.

Step 3c: Run Analysis

# creating empty pref dataset

pref0 = pref*0; rownames(pref0) = NULL

JSM(mydata, pref0) # p-map without prefs information

The above code will generate a p-map (without the preference vectors). Should look like the image below (click for larger image):

However, to make true joint-space maps (JSMs), wherein the preference vectors are overlaid atop the p-map, run the one line code below:

JSM(mydata, pref)

That is it. That one function call executes the entire JSM sequence. The result can be seen in the image below.

Again, the JSM function is generic and can be applied to *any* dataset in the input format we just saw to make joint space maps from. Am sure you'll leverage the code for animating your project datasets. Let me or Ankit know in case any assistance is needed in this regard.

4. Session 2 survey Data on Core courses:

Lookup LMS folder 'session 6 files'. Save the data and code files to your machine. Data files are 'courses data.txt' for the raw data on perceptions and courses data prefs.txt' for the preference data with student names on it. Now let the games begin.

# read in data

mydata = read.table(file.choose()) # 'courses data.txt'

head(mydata)

# I hard coded attribute and brand names

attrib.names = c("will.recommend", "persp.change", "conceptual.value.add", "practical.relevance", "interest.sustained", "difficulty.level");

brand.names = c("GSB", "INVA", "MGTO", "SAIT")

Should you try using your project data or some other dataset, you'll need to enter the brand and attribute names for that dataset in the same order in which they appear in the dataset, separately as given above.I then wrote a simple function, titled 'pmap.inp()' to denote "p-map input", to transform the raw data into a brands-attributes average peceptions table. Note that the below code is specific to the first set of columns being the preferences data.

# construct p-map input matrices using pmap.inp() func

pmap.inp = function(mydata, attrib.names, brand.names){ #> pmap.inp() func opens

a1 = NULL

for (i1 in 1:length(attrib.names)){

start = (i1-1)*length(brand.names)+1; stop = i1*length(brand.names);

a1 = rbind(a1, apply(mydata[,start:stop], 2, mean)) } # i1 loop ends

rownames(a1) = attrib.names; colnames(a1) = brand.names

a1 } # pmap.inp() func ends

a1 = pmap.inp(mydata, attrib.names, brand.names)

And now, we're ready to run the analysis. First the p-mapo without the prefences and then the full JSM.

# now run the JSM func on data

percep = t(a1[2:nrow(a1),]); percep

# prefs = mydata[, 1:length(brand.names)]

prefs = read.table(file.choose(), header = TRUE) # 'courses data prefs.txt'

prefs1 = prefs*0; rownames(prefs1) = NULL # null preferences doc created

JSM(percep, prefs1) # for p-map sans preferences

Should produce the p-map below:

And the one-line JSM run:

JSM(percep, prefs) # for p-map with preference data

Should produce the JSM below:

Follow the rest of the HW code given to run segment-wise JSMs in the same fashion.

5. Running MDS code with Car Survey Data:

The code is in 'R code HW dataset JSMs.txt' in LMS folder 'session 6 files'. The data are in 'mds car data raw.txt'. Read them in and follow the instructions here.

# --------------------- #
### --- MDS code ---- ###
# --------------------- #

rm(list = ls()) # clear workspace

mydata = read.table(file.choose(), header = TRUE) # 'mds car data raw.txt'

dim(mydata) # view dimension of the data matrix

brand.names = c("Hyundai", "Honda", "Fiat", "Ford", "Chevrolet", "Toyota", "Nissan", "TataMotors", "MarutiSuzuki")

Note that I have hard-coded the brand names into 'brand.names' If you want to use this MDS code for another dataset (for your project, say) then you'll have to likewise hard-code the brand.names in.Next, I defined a function called run.mds() that takes as input the raw data and the brand names vector, runs the analysis and outputs the MDS map. Cool, or what..

### --- build user define func run.mds --- ###

run.mds = function(mydata, brand.names){

# build distance matrix # k = length(brand.names)

dmat = matrix(0, k, k)

for (i1 in 1:(k-1)){ a1 = grepl(brand.names[i1], colnames(mydata));

for (i2 in (i1+1):k){a2 = grepl(brand.names[i2], colnames(mydata));
# note use of Regex here

a3 = a1*a2;

a4 = match(1, a3);

dmat[i1, i2] = mean(mydata[, a4]);

dmat[i2, i1] = dmat[i1, i2] } #i2 ends

} # i1 ends

colnames(dmat) = brand.names;

rownames(dmat) = brand.names

### --- run metric MDS --- ###

d = as.dist(dmat)

# Classical MDS into k dimensions #

fit = cmdscale(d,eig=TRUE, k=2) # cmdscale() is core MDS func

fit # view results

# plot solution #

x = fit$points[,1];
y = fit$points[,2];

plot(x, y, xlab="Coordinate 1", ylab="Coordinate 2", main="Metric MDS", xlim = c(floor(min(x)), ceiling(max(x))), ylim = c(floor(min(y)), ceiling(max(y))), type="p",pch=19, col="red");

text(x, y, labels = rownames(fit$points), cex=1.1, pos=1);

abline(h=0); abline(v=0)# horiz and vertical lines drawn

} # run.mds func ends

Time now to finally invoke the run.mds func and get the analysis results:

# run MDS on raw data (before segmenting)

run.mds(mydata, brand.names)

The resulting MDS map looks like this:

OK, that's quite a bit now for classwork replication. Let me know if any code anywhere is not running etc due to any issues.

###################################

6. Session 6 HW:

This HW is also a group submission. You will need to co-operate with the rest of our group to get it done.

  • JSM based homework:
  • Collect basic demographic information about your group mates - #yrs of workex, previous industry, educational qualifications, intended major etc.
  • Run individual level JSM analysis on each of your team mates (and youself) using the code below (place appropriate name in student.name = c("") in that code)
  • Compare the JSMs you obtain - what salient similarities and differences do you see?
  • Now, using the demographic data you have collected, speculate on which demographic characteristics are best able to explain at least some of the similarities and differences you see.
  • Place (i) the 4 JSms, (ii) your list of salient similarities and differences (preferably in tabular form), (iii) the demographic profile of each group member (again, in tabular form) and (iv) the subset of demographic variables that best explain the JSMs in a PPT.
  • MDS based homework:
  • Construct individual level MDS maps for yourself and your group members.
  • Interpret them. In particular try to see what the axes might mean.
  • Interpret the clusters of similar brands (brands bunched close together) in terms of what characteristics are common among them.
  • Collate your (i) MDS plots, (ii) axes interpretation on each plot and (iii) similarity cluster interpretation on each plot into a PPT
  • Submit one PPT for both parts of your homework. Title slide should contain group name and member names + PGIDs. Name the slide as _session6HW.pptx

  • Note: If you don't find your name in the dataset, use a friend's observation (and demographic data) instead.
Use code below to draw individual level JSM plots:
student.name = c("Himanshu") # say, student's name is Himanshu
# retain only that row in the raw data which has name 'Himanshu'

mydata.test = mydata[(rownames(prefs) == student.name),]

# run the pmap.inp() func to build avg perceptions table

a1.test = pmap.inp(mydata.test, attrib.names, brand.names);

percep.test = t(a1.test[2:nrow(a1.test),]);

# introduce a small perturbation lest matrix not be of full rank
percep.test = percep.test + matrix(rnorm(nrow(percep.test)*ncol(percep.test))*0.01, nrow(percep.test), ncol(percep.test));

prefs.test = prefs[(rownames(prefs) == student.name),]; prefs.test

# run analysis on percep.test and prefs.test

JSM(percep.test, prefs.test)

Use code below to run individual level MDS plots. Just place the apropriate student.name and run.

student.name = c("Himanshu") #change student name as reqd
# retain only that row in raw data with name Himanshu

mydata.test = mydata[(rownames(mydata) == student.name),];

# run analysis and save result by copy pasting onto PPT

run.mds(mydata.test, brand.names)

HW deadline is 9-Nov Saturday midnight. That's it for now. Contact me with queries if any.Sudhir

No comments:

Post a Comment

Constructive feedback appreciated. Please try to be civil, as far as feasible. Thanks.