Analyses Not from Konigsberg L, and Frankenberg S. 2016. Postmarital residence analysis. In: Pilloud MA, and Hefner JT, editors. Biological distance analysis: Forensic and bioarchaeological perspectives. New York: Elsevier Inc. p 335-347.

This book chapter, which is about variation in discrete traits, made reference to some R code for such analyses using metric traits.  That R code was on an old server which is no longer running.  As a consequence, I am providing the code below before giving the code that is directly relevant to the book chapter.  In what follows, copy and paste the green text into your Rgui.


Petersen (2002)

Petersen (2002) gives a parametric and nonparametric bootstrap test as alternatives to an F-test he previously gave for testing equality of two variance-covariance matrices.  See the 2002 reference below for a description of these tests as well as prior literature (e.g., Key and Jantz 1990a, 1990b, Zhivotovsky 1988).

 

Petersen HC (2002)  On statistical methods for comparison of intrasample morphometric variability: Zalavár revisted.  Am J Phys Anthropol 113:79-84.

First copy and paste the following into your “Rgui” in order to get some data from the Howells' craniometric data:

download.file('http://faculty.las.illinois.edu/lylek/Mismatch/oslo.txt','oslo.txt')
oslo<-dget('oslo.txt')
download.file(
'http://faculty.las.illinois.edu/lylek/Mismatch/berg.txt','berg.txt')
berg<-dget('berg.txt')
file.remove('oslo.txt');file.remove('berg.txt')

At the prompt type “oslo” and “berg” (without quotes) to make sure you got it right.
Now mark and copy the following text into your “Rgui”:

zhivo<-function (h=berg,w=oslo)
{
SGVDF<-function (DF,p,SGV)
#########################################################################
#       This function takes the DF, p, and SGV and returns the corrected
#       DF and c*SGV (following Zhivotovsky, 1988; per Petersen 2000
#       correction)
########################################################################
{
        gvec<-rep(0,p)
        for(i in 1:p) {
               gvec[i]<-gamma(1/p+(DF-i+1)/2)/gamma((DF-i+1)/2)
                      }
        m1<-2*prod(gvec)
        for(i in 1:p) {
               gvec[i]<-gamma(2/p+(DF-i+1)/2)/gamma((DF-i+1)/2)
                      }
        m2<-4*prod(gvec)
        DFc<-2*m1^2/(m2-m1^2)
        cSGV<-DF/m1*SGV
        return(list(DFc=DFc,cSGV=cSGV))
}
#####################################################################
p<-NCOL(h)
det.ratio<-det(cov(h))/det(cov(w))
SGVh<-det(cov(h))^(1/p)
SGVw<-det(cov(w))^(1/p)
nw<-NROW(w)
nh<-NROW(h)
DFh<-nh-1
DFw<-nw-1
SGVh<-SGVDF(DFh,p,SGVh)
SGVw<-SGVDF(DFw,p,SGVw)
Fratio<-SGVh$cSGV/SGVw$cSGV
p<-pf(Fratio,SGVh$DFc,SGVw$DFc,lower.tail=F)
return(list(det.ratio=det.ratio,Fratio=Fratio,p=p))
}

Now type zhivo() at the R > prompt and you will get the following:

> zhivo()
$det.ratio
[1] 7.506352
 
$Fratio
[1] 1.220779
 
$p
[1] 0.01381720
 
In the above, “det.ratio” is the ratio of determinants of the two variance-covariance matrices, “Fratio” is the F-test, and “p” is the probability value for the F-test.  In the call to “zhivo” you can specify different datasets, for example “zhivo(samp1,samp2)” or “zhivo(h=samp1,w=samp2).”


Now it is time to do a nonparametric bootstrap, so paste the following:
 

bootnon<-function (h=berg,w=oslo,Nboot=9,seed='NonRan')
{

set.seed(as.integer(Sys.time()))
if(seed=='NonRan') set.seed(42)
boot<-rep(0,Nboot+1)
p<-NCOL(h)
nw<-NROW(w)
nh<-NROW(h)
W<-w-rep(1,nw)%o%apply(w,2,mean)
H<-h-rep(1,nh)%o%apply(h,2,mean)
hw<-rbind(H,W)
 
boot[1]<-det(cov(hw[1:nh,]))/det(cov(hw[(nh+1):(nh+nw),]))
 
for (i in 2:(Nboot+1))  {
bootit<-sample(1:(nw+nh),replace=T)
hwb<-hw[bootit,1:p]
boot[i]<-det(cov(hwb[1:nh,]))/det(cov(hwb[(nh+1):(nh+nw),]))
      }
return(boot)
}

and run as “bootnon()”.  This will spit back a 10-element vector:
 
> bootnon()
[1] 7.5063520 2.3276142 2.6624329 4.2080064 1.0223504 0.1738658 0.7785375 1.2931999 1.7586308 0.8731364

where the first element (=
7.5063520 ) is the observed determinant ratio and the following nine are bootstrapped.  Nine bootstraps is pretty pathetic, so you’ll want to try something meatier once your sure things are working correctly, like:
 
> bootnon2<-bootnon(Nboot=999,seed='Ran')
 
This will store the vector, then you can use “sum(bootnon2>=bootnon2[1])/1000” to get the p-value.  The seed='Ran' part uses the computer clock for a seed to the random generator (otherwise the random number sequence always starts at 42).

Now it is time to do a parametric bootstrap, so paste the following:

bootwish<- function (h=berg,w=oslo,Nboot=9,seed='NonRan')
{
set.seed(as.integer(Sys.time()))
if(seed=='NonRan') set.seed(42)


rwishart2<-function(df,SqrtSigma)
#################################################################
# rwishart2 simulates from a wishart distribution using the
# Cholesky decomposition of a variance-covariance matrix
#################################################################
{
p<-NROW(SqrtSigma)
Z <- matrix(0, p, p)
diag(Z) <- sqrt(rchisq(p, df:(df-p+1)))
pseq <- 1:(p-1)
Z[rep(p*pseq, pseq) + unlist(lapply(pseq, seq))] <- rnorm(p*(p-1)/2)
crossprod(Z %*% SqrtSigma)
}
#################################################################
 
boot<-rep(0,Nboot+1)
p<-NCOL(h)
nw<-NROW(w)
nh<-NROW(h)
DFh<-nh-1
DFw<-nw-1
 
boot[1]<-det(cov(h))/det(cov(w))
Sp<-(cov(h)*DFh+cov(w)*DFw)/(DFh+DFw)
SqrtSp<-chol(Sp)
 
for (i in 2:(Nboot+1))  {
boot[i]<-det(rwishart2(DFh,SqrtSp)/DFh)/det(rwishart2(DFw,SqrtSp)/DFw)
      }
return(boot)
}
 

and run as “bootwish()”.  This will spit back a 10-element vector:
 
> bootwish()
[1]  7.5063520  1.0086355  0.1061152  0.4056361 10.2281551  0.2542374  0.4334881  0.2216340  0.5006118  1.3059963

where the first element (=
7.5063520 ) is the observed determinant ratio and the following nine are bootstrapped.  Nine bootstraps is pretty pathetic, so you’ll want to try something meatier once your sure things are working correctly, like:
 
> bootwish2<-bootwish(Nboot=999,seed='Ran')
 
This will store the vector, then you can use “sum(bootwish2>=bootwish2[1])/1000” to get the p-value.  The seed='Ran' part uses the computer clock for a seed to the random generator (otherwise the random number sequence always starts at 42).


Analyses from Konigsberg L, and Frankenberg S. 2016. Postmarital residence analysis. In: Pilloud MA, and Hefner JT, editors. Biological distance analysis: Forensic and bioarchaeological perspectives. New York: Elsevier Inc. p 335-347.


You will need the packages "ape" and "shape."  Then copy and paste the green stuff below.



read.files=function ()
{
library(ape)
download.file('http://faculty.las.illinois.edu/lylek/Mismatch/Sites.zip','Sites.zip')
unzip('Sites.zip')
file.remove('Sites.zip')
EZ2F<<-read.dna('EZ2F.txt');EZ2M<<-read.dna('EZ2M.txt');EZF<<-read.dna('EZF.txt')
EZM<<-read.dna('EZM.txt');Females<<-read.dna('Females.txt');GIF<<-read.dna('GIF.txt')
GIM<<-read.dna('GIM.txt');HNF<<-read.dna('HNF.txt');HNM<<-read.dna('HNM.txt')

HRF<<-read.dna('HRF.txt');HRM<<-read.dna('HRM.txt');KOF<<-read.dna('KOF.txt')
KOM<<-read.dna('KOM.txt');LDF<<-read.dna('LDF.txt');LDM<<-read.dna('LDM.txt')
Males<<-read.dna('Males.txt');PK2F<<-read.dna('PK2F.txt');PK2M<<-read.dna('PK2M.txt')
PKF<<-read.dna('PKF.txt');PKlunkF<<-read.dna('PKlunkF.txt');PKlunkM<<-read.dna('PKlunkM.txt')
PKM<<-read.dna('PKM.txt');RYF<<-read.dna('RYF.txt');RYM<<-read.dna('RYM.txt')
SH1F<<-read.dna('SH1F.txt');SH1M<<-read.dna('SH1M.txt');SHF<<-read.dna('SHF.txt')
SHM<<-read.dna('SHM.txt');YO1F<<-read.dna('YO1F.txt');YO1M<<-read.dna('YO1M.txt')

YOF<<-read.dna('YOF.txt');YOM<<-read.dna('YOM.txt')


# Remove *.txt files

file.remove('EZ2F.txt');file.remove('EZ2M.txt');file.remove('EZF.txt')
file.remove('EZM.txt');file.remove('Females.txt');file.remove('GIF.txt')
file.remove('GIM.txt');file.remove('HNF.txt');file.remove('HNM.txt')

file.remove('HRF.txt');file.remove('HRM.txt');file.remove('KOF.txt')
file.remove('KOM.txt');file.remove('LDF.txt');file.remove('LDM.txt')
file.remove('Males.txt');file.remove('PK2F.txt');file.remove('PK2M.txt')
file.remove('PKF.txt');file.remove('PKlunkF.txt');file.remove('PKlunkM.txt')
file.remove('PKM.txt');file.remove('RYF.txt');file.remove('RYM.txt')
file.remove('SH1F.txt');file.remove('SH1M.txt');file.remove('SHF.txt')
file.remove('SHM.txt');file.remove('YO1F.txt');file.remove('YO1M.txt')

file.remove('YOF.txt');file.remove('YOM.txt')

}


read.files()


Figure 1


Histograms of 10,000 bootstrap samples (across nucleotides) for mean number of mtDNA pairwise differences within
males and within females for Bolnick and Smith’s (2007) Middle Woodland samples from the Pete Klunk Mound
Group. The dashed vertical lines are the observed male and female values.


Fig.01 = function ()
{
 library(ape)
 library(MASS)
 set.seed(12345)

 opar = par(no.readonly = T)

 on.exit(par(opar))

 par(mfrow=c(2,1))
 pairw=0
 for(i in 1:10000) pairw[i] = mean(dist.dna(Males[,sample(1:351,replace=T)],"N"))
 truehist(pairw,nb=60,prob=T,xlab=' Mean No. Pairwise Differences',
      ylab='Probability',col='white',
      main='Male DNA sequence data')
 actual=mean(dist.dna(Males,"N"))
 lines(rep(actual,2),c(0,1000),lwd=2,lty=2)
 print(quantile(pairw,c(0.025,0.5,0.975)))
 print(mean(pairw))

 for(i in 1:10000) pairw[i] = mean(dist.dna(Females[,sample(1:351,replace=T)],"N"))
 truehist(pairw,nb=60,prob=T,xlab=' Mean No. Pairwise Differences',
      ylab='Probability',col='white',
      main='Female DNA sequence data')
 actual=mean(dist.dna(Females,"N"))
 lines(rep(actual,2),c(0,1000),lwd=2,lty=2)

 print(quantile(pairw,c(0.025,0.5,0.975)))
 print(mean(pairw))

}

Fig.01()

Figure 2

Empirical cumulative density representation of the 10,000 bootstrap samples for males in Fig. 18.1. The 2.5th, 50th,
and 97.5th percentile values are marked, as is the actual observed value.


Fig.02=function (nboots=10000)
{
 library(ape)
 library(shape)
 set.seed(12345)
 pairw=0
 actual=mean(dist.dna(Males,"N"))
 for(i in 1:nboots) pairw[i] = mean(dist.dna(Males[,sample(1:351,replace=T)],"N"))
 lo=min(pairw)
 hi=max(pairw)
 to.plot=ecdf(pairw)
 pnts=knots(to.plot)
 lo=pnts[2]
 hi=pnts[length(pnts)-1]
 height = sum(actual>pairw)/nboots
 plot.stepfun(to.plot,xlab=' Mean No. Pairwise Differences',
   ylab='Cumulative density',do.points=F,verticals=T,
   main='Cumulative density mean pair-wise differences (Males)',
   xlim=c(2,12),ylim=c(0,1))
 lines(c(0,lo),rep(0,2))
 lines(c(hi,14),rep(1,2))
 lines(rep(actual,2),c(0,height),lwd=2,lty=2)
 CI=as.vector(quantile(pairw,probs=c(0.025,0.5,0.975)))
 lines(rep(CI[1],2),c(0,0.025),lwd=2)
 lines(rep(CI[2],2),c(0,0.5),lwd=2)
 lines(rep(CI[3],2),c(0,0.975),lwd=2)
 lines(c(0,CI[1]),rep(0.025,2),lty=2)
 lines(c(0,CI[2]),rep(0.5,2),lty=2)
 lines(c(0,CI[3]),rep(0.975,2),lty=2)
 text(1.8,.04,'0.025',adj=c(0,0))
 text(1.8,0.99,'0.975',adj=c(0,0))
 text(1.8,0.51,'Median (0.5)',adj=c(0,0))
 print(round(CI,3))
 Arrows(7.4453,.3796,6.7031,.3250,arr.adj=1)
 text(7.6,.38,'actual',adj=c(0,.5))
}

Fig.02()

Figure 3

Empirical cumulative density plot of the 10,000 bootstrap samples where the plotted statistic is the ratio of male-tofemale
mean pairwise differences.


Fig.03 = function (nboots=10000)
{
 library(ape)
 set.seed(123456)
 actual=mean(dist.dna(Males,'N'))/mean(dist.dna(Females,'N'))
 pairw=0
 for(i in 1:nboots) {
   boot = sample(1:351,replace=T)
   pairw[i] = mean(dist.dna(Males[,boot],"N"))/
              mean(dist.dna(Females[,boot],"N"))
 }

 height = sum(actual>pairw)/nboots

 lo=min(pairw)
 hi=max(pairw)
 to.plot=ecdf(pairw)
 pnts=knots(to.plot)
 lo=pnts[2]
 hi=pnts[length(pnts)-1]
 plot.stepfun(to.plot,xlab=' Ratio of Male to Female Mean Pairwise Differences',
   ylab='Cumulative density',do.points=F,verticals=T,
   main='Bootstrap Ratio',ylim=c(0,1),xlim=c(0.5,2))
 lines(rep(actual,2),c(0,height),lwd=2,lty=2)
 lines(c(0,lo),rep(0,2))
 lines(c(hi,14),rep(1,2))
 lines(rep(actual,2),c(0,height),lwd=2,lty=2)
 lines(rep(1,2),c(0,1000),lwd=2,lty=3)
 CI=as.vector(quantile(pairw,probs=c(0.025,0.5,0.975)))
 lines(rep(CI[1],2),c(0,0.025),lwd=2)
 lines(rep(CI[3],2),c(0,0.975),lwd=2)
 print(round(CI,3))
 legend('topleft',legend=c('Null (ratio = 1)','2.5 and 97.5 \n percentiles','actual'),
     bty='n',lty=c(3,1,2),lwd=2,cex=.75)
 abline(h=0)

}

Fig.03()

Figure 4

Empirical cumulative density plot of 10,000 bootstrap samples for the Pete Klunk Middle Woodland Site where the
plotted statistic is the ratio of male-to-female mean pairwise differences for cranial discrete traits. Note that the
observed ratio value (0.946) is near the 1.0 ratio, indicating equivalent variation in the sexes.


Fig.04 = function (nboots=10000)
{
library(ape)
set.seed(123456)
 actual=mean(dist.dna(PKlunkM,pairwise.deletion=T,'N'))/
        mean(dist.dna(PKlunkF,pairwise.deletion=T,'N'))
print(actual)
 pairw=0
 for(i in 1:nboots) {
   boot = sample(1:23,replace=T)
   pairw[i] = mean(dist.dna(PKlunkM[,boot],pairwise.deletion=T,"N"))/
              mean(dist.dna(PKlunkF[,boot],pairwise.deletion=T,"N"))
 }

 height = sum(actual>pairw)/nboots

 lo=min(pairw)
 hi=max(pairw)
 to.plot=ecdf(pairw)
 pnts=knots(to.plot)
 lo=pnts[2]
 hi=pnts[length(pnts)-1]
 plot.stepfun(to.plot,xlab=' Ratio of Male to Female Mean Pairwise Differences',
   ylab='Cumulative density',do.points=F,verticals=T,
   main='Pete Klunk Middle Woodland (discrete traits)',ylim=c(0,1),xlim=c(0.5,1.5))
 lines(rep(actual,2),c(0,height),lwd=2,lty=2)
 lines(c(0,lo),rep(0,2))
 lines(c(hi,14),rep(1,2))
 lines(rep(1,2),c(0,1000),lwd=2,lty=3)
 CI=as.vector(quantile(pairw,probs=c(0.025,0.975)))
 lines(rep(CI[1],2),c(0,0.025),lwd=2)
 lines(rep(CI[2],2),c(0,0.975),lwd=2)
 legend('topleft',legend=c('Null (ratio = 1)','2.5 and 97.5 percentiles','actual'),
     bty='n',lty=c(3,1,2),lwd=2,cex=.75)
 abline(h=0)
}

Fig.04()

Figure 5

Empirical cumulative density plot of 10,000 bootstrap samples for 11 pre-Mississippian sites where the plotted statistic
is the ratio of male-to-female mean pairwise differences for cranial discrete traits.

Thanks to Dr. Matthew C. Velasco for catching an error in the previous version of this script, which has now been corrected following his comments.

Fig.05 = function (nboots=10000)
{
library(ape)
set.seed(123456)
NM=c(NROW(EZM),NROW(GIM),NROW(PKM),NROW(RYM),NROW(KOM),NROW(HNM),NROW(LDM),
     NROW(EZ2M),NROW(PK2M),NROW(SH1M),NROW(YO1M))
Np.M=sum(NM*(NM-1)/2)

NF=c(NROW(EZF),NROW(GIF),NROW(PKF),NROW(RYF),NROW(KOF),NROW(HNF),NROW(LDF),
     NROW(EZ2F),NROW(PK2F),NROW(SH1F),NROW(YO1F))   
Np.F=sum(NF*(NF-1)/2)

   Sumd.M = sum(dist.dna(EZM,pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(GIM,pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(PKM,pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(RYM,pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(KOM,pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(HNM,pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(LDM,pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(EZ2M,pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(PK2M,pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(SH1M,pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(YO1M,pairwise.deletion=T,"N"))

   Sumd.F = sum(dist.dna(EZF,pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(GIF,pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(PKF,pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(RYF,pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(KOF,pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(HNF,pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(LDF,pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(EZ2F,pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(PK2F,pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(SH1F,pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(YO1F,pairwise.deletion=T,"N"))

 actual = (Sumd.M/Np.M)/(Sumd.F/Np.F)

 pairw=0
 for(i in 1:nboots) {
   boot = sample(1:23,replace=T)
   Sumd.M = sum(dist.dna(EZM[,boot],pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(GIM[,boot],pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(PKM[,boot],pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(RYM[,boot],pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(KOM[,boot],pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(HNM[,boot],pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(LDM[,boot],pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(EZ2M[,boot],pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(PK2M[,boot],pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(SH1M[,boot],pairwise.deletion=T,"N"))
   Sumd.M = Sumd.M + sum(dist.dna(YO1M[,boot],pairwise.deletion=T,"N"))

   Sumd.F = sum(dist.dna(EZF[,boot],pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(GIF[,boot],pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(PKF[,boot],pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(RYF[,boot],pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(KOF[,boot],pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(HNF[,boot],pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(LDF[,boot],pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(EZ2F[,boot],pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(PK2F[,boot],pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(SH1F[,boot],pairwise.deletion=T,"N"))
   Sumd.F = Sumd.F + sum(dist.dna(YO1F[,boot],pairwise.deletion=T,"N"))

   pairw[i] = (Sumd.M/Np.M)/(Sumd.F/Np.F)
 }

 height = sum(actual>pairw)/nboots

 lo=min(pairw)
 hi=max(pairw)
 to.plot=ecdf(pairw)
 pnts=knots(to.plot)
 lo=pnts[2]
 hi=pnts[length(pnts)-1]
 plot.stepfun(to.plot,xlab='Ratio of Male to Female Within-Site Mean Pairwise Differences',
   ylab='Cumulative density',do.points=F,verticals=T,
   main='Eleven Pre-Mississippian Sites',ylim=c(0,1),xlim=c(0.7,1.1))
 mtext('(Hacker South Mound 2 classified as Mississippian)')
 lines(rep(actual,2),c(0,height),lwd=2,lty=2)
 lines(c(0,lo),rep(0,2))
 lines(c(hi,14),rep(1,2))
 lines(rep(1,2),c(0,1000),lwd=2,lty=3)
 CI=as.vector(quantile(pairw,probs=c(0.025,0.975)))
 lines(rep(CI[1],2),c(0,0.025),lwd=2)
 lines(rep(CI[2],2),c(0,0.975),lwd=2)
 legend('topleft',legend=c('Null (ratio = 1)','2.5 and 97.5 percentiles','actual'),
     bty='n',lty=c(3,1,2),lwd=2,cex=.75)
 abline(h=0)
 print(c(CI[1],actual,CI[2]))
}

Fig.05()

Figure 6

Empirical cumulative density plot of 10,000 bootstrap samples for three Mississippian sites where the plotted statistic is
the ratio of male-to-female mean pairwise differences for cranial discrete traits.

Thanks to Dr. Matthew C. Velasco for catching an error in the previous version of this script, which has now been corrected following his comments.

Fig.06 = function (nboots=10000)
{
library(ape)
set.seed(123456)
NM=c(NROW(HRM),NROW(SHM),NROW(YOM))
Np.M=sum(NM*(NM-1)/2)
NF=c(NROW(HRF),NROW(SHF),NROW(YOF))
Np.F=sum(NF*(NF-1)/2)

Sumd.M = sum(dist.dna(HRM,pair=T,"N"))
Sumd.M = Sumd.M + sum(dist.dna(SHM,pair=T,"N"))
Sumd.M = Sumd.M + sum(dist.dna(YOM,pair=T,"N"))

Sumd.F = sum(dist.dna(HRF,pair=T,"N"))
Sumd.F = Sumd.F + sum(dist.dna(SHF,pair=T,"N"))
Sumd.F = Sumd.F + sum(dist.dna(YOF,pair=T,"N"))

actual = (Sumd.M/Np.M)/(Sumd.F/Np.F)

 pairw=0
 for(i in 1:nboots) {
   boot = sample(1:23,replace=T)
    Sumd.M = sum(dist.dna(HRM[,boot],pair=T,"N"))
    Sumd.M = Sumd.M + sum(dist.dna(SHM[,boot],pair=T,"N"))
    Sumd.M = Sumd.M + sum(dist.dna(YOM[,boot],pair=T,"N"))

    Sumd.F = sum(dist.dna(HRF[,boot],pair=T,"N"))
    Sumd.F = Sumd.F + sum(dist.dna(SHF[,boot],pair=T,"N"))
    Sumd.F = Sumd.F + sum(dist.dna(YOF[,boot],pair=T,"N"))
   pairw[i] = (Sumd.M/Np.M)/(Sumd.F/Np.F)
 }
 
 height = sum(actual>pairw)/nboots
 lo=min(pairw)
 hi=max(pairw)
 to.plot=ecdf(pairw)
 pnts=knots(to.plot)
 lo=pnts[2]
 hi=pnts[length(pnts)-1]
 plot.stepfun(to.plot,xlab='Ratio of Male to Female Within-Site Mean Pairwise Differences',
   ylab='Cumulative density',do.points=F,verticals=T,
   main='Three Mississippian Period Sites',ylim=c(0,1),xlim=c(0.8,1.4))
 mtext('(Hacker South Mound 2 classified as Mississippian)')
 lines(rep(actual,2),c(0,height),lwd=2,lty=2)
 lines(c(0,lo),rep(0,2))
 lines(c(hi,14),rep(1,2))
 lines(rep(1,2),c(0,1000),lwd=2,lty=3)
 CI=as.vector(quantile(pairw,probs=c(0.025,0.975)))
 lines(rep(CI[1],2),c(0,0.025),lwd=2)
 lines(rep(CI[2],2),c(0,0.975),lwd=2)


 legend('topleft',legend=c('Null (ratio = 1)','2.5 and 97.5 \n percentiles','actual'),
     bty='n',lty=c(3,1,2),lwd=2,cex=.75)
 abline(h=0)
 print(c(CI[1],actual,CI[2]))
}


Fig.06()