--- /dev/null
+!*********************** Contents ****************************************
+!* Sample driver program, VAX-11 Fortran; **********************************
+!* HC: O(n^2) time, O(n^2) space hierarchical clustering, Fortran 77 *******
+!* HCASS: determine cluster-memberships, Fortran 77. ***********************
+!* HCDEN: draw upper part of dendrogram, VAX-11 Fortran. *******************
+!* Sample data set: last 36 lines. *****************************************
+!***************************************************************************
+! REAL DATA(18,16),CRIT(18),MEMBR(18)
+! REAL CRITVAL(9)
+! INTEGER IA(18),IB(18)
+! INTEGER ICLASS(18,9),HVALS(9)
+! INTEGER IORDER(9),HEIGHT(9)
+! DIMENSION NN(18),DISNN(18)
+! REAL D(153)
+! LOGICAL FLAG(18)
+! IN ABOVE, 18=N, 16=M, 9=LEV, 153=N(N-1)/2.
+!
+!
+! OPEN(UNIT=21,STATUS='OLD',FILE='SPECTR.DAT')
+!
+!
+! N = 18
+! M = 16
+! DO I=1,N
+! READ(21,100)(DATA(I,J),J=1,M)
+! ENDDO
+! 100 FORMAT(8F7.1)
+!
+!
+! LEN = (N*(N-1))/2
+! IOPT=1
+! CALL HC(N,M,LEN,IOPT,DATA,IA,IB,CRIT,MEMBR,NN,DISNN,FLAG,D)
+!
+!
+! LEV = 9
+! CALL HCASS(N,IA,IB,CRIT,LEV,ICLASS,HVALS,IORDER,CRITVAL,HEIGHT)
+!
+!
+! CALL HCDEN(LEV,IORDER,HEIGHT,CRITVAL)
+!
+!
+! END
+!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++C
+! C
+! HIERARCHICAL CLUSTERING using (user-specified) criterion. C
+! C
+! Parameters: C
+! C
+!removed DATA(N,M) input data matrix, C
+! DISS(LEN) dissimilarities in lower half diagonal C
+! storage; LEN = N.N-1/2, C
+! IOPT clustering criterion to be used, C
+! IA, IB, CRIT history of agglomerations; dimensions C
+! N, first N-1 locations only used, C
+! MEMBR, NN, DISNN vectors of length N, used to store C
+! cluster cardinalities, current nearest C
+! neighbour, and the dissimilarity assoc. C
+! with the latter. C
+! FLAG boolean indicator of agglomerable obj./ C
+! clusters. C
+! C
+! F. Murtagh, ESA/ESO/STECF, Garching, February 1986. C
+! C
+!------------------------------------------------------------C
+ module hc_
+!-----------------------------------------------------------------------------
+ use io_units
+ use names
+ use clust_data
+ implicit none
+!-----------------------------------------------------------------------------
+!
+!
+!-----------------------------------------------------------------------------
+ contains
+!-----------------------------------------------------------------------------
+! hc.f
+!-----------------------------------------------------------------------------
+
+ SUBROUTINE HC(N,M,LEN,IOPT,IA,IB,CRIT,MEMBR,NN,DISNN,&
+ FLAG,DISS)
+ integer :: N,M,LEN,IOPT
+ REAL(kind=4) :: MEMBR(N)
+ REAL(kind=4) :: DISS(LEN)
+ INTEGER :: IA(N),IB(N)
+ REAL(kind=4) :: CRIT(N)
+ integer,DIMENSION(N) :: NN
+ real(kind=4),dimension(N) ::DISNN
+ LOGICAL :: FLAG(N)
+ REAL(kind=4) INF
+ DATA INF /1.E+20/
+ integer :: I,J,NCL,IND,IM,JM,I2,J2,K,IND1,IND2,IND3,JJ
+ real(kind=8) :: DMIN,X,XX
+!
+! Initializations
+!
+ DO I=1,N
+ MEMBR(I)=1.
+ FLAG(I)=.TRUE.
+ ENDDO
+ NCL=N
+!
+! Construct dissimilarity matrix
+!
+ DO I=1,N-1
+ DO J=I+1,N
+ IND=IOFFSET(N,I,J)
+!input DISS(IND)=0.
+!input DO K=1,M
+!input DISS(IND)=DISS(IND)+(DATA(I,K)-DATA(J,K))**2
+!input ENDDO
+ IF (IOPT.EQ.1) DISS(IND)=DISS(IND)/2.
+! (Above is done for the case of the min. var. method
+! where merging criteria are defined in terms of variances
+! rather than distances.)
+ ENDDO
+ ENDDO
+!
+! Carry out an agglomeration - first create list of NNs
+!
+ DO I=1,N-1
+ DMIN=INF
+ DO J=I+1,N
+ IND=IOFFSET(N,I,J)
+ IF (DISS(IND).GE.DMIN) GOTO 500
+ DMIN=DISS(IND)
+ JM=J
+ 500 CONTINUE
+ ENDDO
+ NN(I)=JM
+ DISNN(I)=DMIN
+ ENDDO
+!
+ 400 CONTINUE
+! Next, determine least diss. using list of NNs
+ DMIN=INF
+ DO I=1,N-1
+ IF (.NOT.FLAG(I)) GOTO 600
+ IF (DISNN(I).GE.DMIN) GOTO 600
+ DMIN=DISNN(I)
+ IM=I
+ JM=NN(I)
+ 600 CONTINUE
+ ENDDO
+ NCL=NCL-1
+!
+! This allows an agglomeration to be carried out.
+!
+ I2=MIN0(IM,JM)
+ J2=MAX0(IM,JM)
+ IA(N-NCL)=I2
+ IB(N-NCL)=J2
+ CRIT(N-NCL)=DMIN
+!
+! Update dissimilarities from new cluster.
+!
+ FLAG(J2)=.FALSE.
+ DMIN=INF
+ DO K=1,N
+ IF (.NOT.FLAG(K)) GOTO 800
+ IF (K.EQ.I2) GOTO 800
+ X=MEMBR(I2)+MEMBR(J2)+MEMBR(K)
+ IF (I2.LT.K) THEN
+ IND1=IOFFSET(N,I2,K)
+ ELSE
+ IND1=IOFFSET(N,K,I2)
+ ENDIF
+ IF (J2.LT.K) THEN
+ IND2=IOFFSET(N,J2,K)
+ ELSE
+ IND2=IOFFSET(N,K,J2)
+ ENDIF
+ IND3=IOFFSET(N,I2,J2)
+ XX=DISS(IND3)
+!
+! WARD'S MINIMUM VARIANCE METHOD - IOPT=1.
+!
+ IF (IOPT.EQ.1) THEN
+ DISS(IND1)=(MEMBR(I2)+MEMBR(K))*DISS(IND1)+ &
+ (MEMBR(J2)+MEMBR(K))*DISS(IND2)- &
+ MEMBR(K)*XX
+ DISS(IND1)=DISS(IND1)/X
+ ENDIF
+!
+! SINGLE LINK METHOD - IOPT=2.
+!
+ IF (IOPT.EQ.2) THEN
+ DISS(IND1)=MIN(DISS(IND1),DISS(IND2))
+ ENDIF
+!
+! COMPLETE LINK METHOD - IOPT=3.
+!
+ IF (IOPT.EQ.3) THEN
+ DISS(IND1)=MAX(DISS(IND1),DISS(IND2))
+ ENDIF
+!
+! AVERAGE LINK (OR GROUP AVERAGE) METHOD - IOPT=4.
+!
+ IF (IOPT.EQ.4) THEN
+ DISS(IND1)=(MEMBR(I2)*DISS(IND1)+MEMBR(J2)*DISS(IND2))/ &
+ (MEMBR(I2)+MEMBR(J2))
+ ENDIF
+!
+! MCQUITTY'S METHOD - IOPT=5.
+!
+ IF (IOPT.EQ.5) THEN
+ DISS(IND1)=0.5*DISS(IND1)+0.5*DISS(IND2)
+ ENDIF
+!
+! MEDIAN (GOWER'S) METHOD - IOPT=6.
+!
+ IF (IOPT.EQ.6) THEN
+ DISS(IND1)=0.5*DISS(IND1)+0.5*DISS(IND2)-0.25*XX
+ ENDIF
+!
+! CENTROID METHOD - IOPT=7.
+!
+ IF (IOPT.EQ.7) THEN
+ DISS(IND1)=(MEMBR(I2)*DISS(IND1)+MEMBR(J2)*DISS(IND2)- &
+ MEMBR(I2)*MEMBR(J2)*XX/(MEMBR(I2)+MEMBR(J2)))/ &
+ (MEMBR(I2)+MEMBR(J2))
+ ENDIF
+!
+ IF (I2.GT.K) GOTO 800
+ IF (DISS(IND1).GE.DMIN) GOTO 800
+ DMIN=DISS(IND1)
+ JJ=K
+ 800 CONTINUE
+ ENDDO
+ MEMBR(I2)=MEMBR(I2)+MEMBR(J2)
+ DISNN(I2)=DMIN
+ NN(I2)=JJ
+!
+! Update list of NNs insofar as this is required.
+!
+ DO I=1,N-1
+ IF (.NOT.FLAG(I)) GOTO 900
+ IF (NN(I).EQ.I2) GOTO 850
+ IF (NN(I).EQ.J2) GOTO 850
+ GOTO 900
+ 850 CONTINUE
+! (Redetermine NN of I:)
+ DMIN=INF
+ DO J=I+1,N
+ IND=IOFFSET(N,I,J)
+ IF (.NOT.FLAG(J)) GOTO 870
+ IF (I.EQ.J) GOTO 870
+ IF (DISS(IND).GE.DMIN) GOTO 870
+ DMIN=DISS(IND)
+ JJ=J
+ 870 CONTINUE
+ ENDDO
+ NN(I)=JJ
+ DISNN(I)=DMIN
+ 900 CONTINUE
+ ENDDO
+!
+! Repeat previous steps until N-1 agglomerations carried out.
+!
+ IF (NCL.GT.1) GOTO 400
+!
+!
+ RETURN
+ END SUBROUTINE HC
+!-----------------------------------------------------------------------------
+!
+!
+ integer FUNCTION IOFFSET(N,I,J)
+! Map row I and column J of upper half diagonal symmetric matrix
+! onto vector.
+ integer :: N,I,J
+ IOFFSET=J+(I-1)*N-(I*(I+1))/2
+ RETURN
+ END FUNCTION IOFFSET
+!-----------------------------------------------------------------------------
+!+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++C
+! C
+! Given a HIERARCHIC CLUSTERING, described as a sequence of C
+! agglomerations, derive the assignments into clusters for the C
+! top LEV-1 levels of the hierarchy. C
+! Prepare also the required data for representing the C
+! dendrogram of this top part of the hierarchy. C
+! C
+! Parameters: C
+! C
+! IA, IB, CRIT: vectors of dimension N defining the agglomer- C
+! ations. C
+! LEV: number of clusters in largest partition. C
+! HVALS: vector of dim. LEV, used internally only. C
+! ICLASS: array of cluster assignments; dim. N by LEV. C
+! IORDER, CRITVAL, HEIGHT: vectors describing the dendrogram, C
+! all of dim. LEV. C
+! C
+! F. Murtagh, ESA/ESO/STECF, Garching, February 1986. C
+! C
+! HISTORY C
+! C
+! Bounds bug fix, Oct. 1990, F. Murtagh. C
+! Inserted line "IF (LOC.GT.LEV) GOTO 58" on line 48. This was C
+! occassioned by incorrect termination of this loop when I C
+! reached its (lower) extremity, i.e. N-LEV. Without the C
+! /CHECK=(BOUNDS) option on VAX/VMS compilation, this inserted C
+! statement was not necessary. C
+!---------------------------------------------------------------C
+ SUBROUTINE HCASS(N,IA,IB,CRIT,LEV,ICLASS,HVALS,IORDER,&
+ CRITVAL,HEIGHT)
+! include 'sizesclu.dat'
+! include 'COMMON.IOUNITS'
+ integer :: N,LEV
+!el integer :: ICLASS(maxconf,maxconf-1)
+ integer :: ICLASS(N,N-1)
+ INTEGER :: IA(N),IB(N),HVALS(LEV),IORDER(LEV),&
+ HEIGHT(LEV)
+ REAL(kind=4) :: CRIT(N),CRITVAL(LEV)
+ integer :: I,J,LOC,LEVEL,ICL,ILEV,NCL,K
+!
+! Pick out the clusters which the N objects belong to,
+! at levels N-2, N-3, ... N-LEV+1 of the hierarchy.
+! The clusters are identified by the lowest seq. no. of
+! their members.
+! There are 2, 3, ... LEV clusters, respectively, for the
+! above levels of the hierarchy.
+!
+ HVALS(1)=1
+ HVALS(2)=IB(N-1)
+ LOC=3
+ DO 59 I=N-2,N-LEV,-1
+ DO 52 J=1,LOC-1
+ IF (IA(I).EQ.HVALS(J)) GOTO 54
+ 52 CONTINUE
+ HVALS(LOC)=IA(I)
+ LOC=LOC+1
+ 54 CONTINUE
+ DO 56 J=1,LOC-1
+ IF (IB(I).EQ.HVALS(J)) GOTO 58
+ 56 CONTINUE
+ IF (LOC.GT.LEV) GOTO 58
+ HVALS(LOC)=IB(I)
+ LOC=LOC+1
+ 58 CONTINUE
+ 59 CONTINUE
+!
+ DO 400 LEVEL=N-LEV,N-2
+ DO 200 I=1,N
+ ICL=I
+ DO 100 ILEV=1,LEVEL
+ 100 IF (IB(ILEV).EQ.ICL) ICL=IA(ILEV)
+ NCL=N-LEVEL
+ ICLASS(I,NCL-1)=ICL
+ 200 CONTINUE
+ 400 CONTINUE
+!
+ DO 120 I=1,N
+ DO 120 J=1,LEV-1
+ DO 110 K=2,LEV
+ IF (ICLASS(I,J).NE.HVALS(K)) GOTO 110
+ ICLASS(I,J)=K
+ GOTO 120
+ 110 CONTINUE
+ 120 CONTINUE
+!
+ WRITE (iout,450) (j,j=2,LEV)
+ 450 FORMAT(4X,' SEQ NOS',8(i2,'CL'),10000(i3,'CL'))
+ WRITE (iout,470) (' ---',j=2,LEV)
+ 470 FORMAT(4X,' -------',10000a4)
+ DO 500 I=1,N
+ WRITE (iout,600) I,(ICLASS(I,J),J=1,LEV-1)
+ 600 FORMAT(I11,8I4,10000i5)
+ 500 CONTINUE
+!
+! Determine an ordering of the LEV clusters (at level LEV-1)
+! for later representation of the dendrogram.
+! These are stored in IORDER.
+! Determine the associated ordering of the criterion values
+! for the vertical lines in the dendrogram.
+! The ordinal values of these criterion values may be used in
+! preference, and these are stored in HEIGHT.
+! Finally, note that the LEV clusters are renamed so that they
+! have seq. nos. 1 to LEV.
+!
+ IORDER(1)=IA(N-1)
+ IORDER(2)=IB(N-1)
+ CRITVAL(1)=0.0
+ CRITVAL(2)=CRIT(N-1)
+ HEIGHT(1)=LEV
+ HEIGHT(2)=LEV-1
+ LOC=2
+ DO 700 I=N-2,N-LEV+1,-1
+ DO 650 J=1,LOC
+ IF (IA(I).EQ.IORDER(J)) THEN
+! Shift rightwards and insert IB(I) beside IORDER(J):
+ DO 630 K=LOC+1,J+1,-1
+ IORDER(K)=IORDER(K-1)
+ CRITVAL(K)=CRITVAL(K-1)
+ HEIGHT(K)=HEIGHT(K-1)
+ 630 CONTINUE
+ IORDER(J+1)=IB(I)
+ CRITVAL(J+1)=CRIT(I)
+ HEIGHT(J+1)=I-(N-LEV)
+ LOC=LOC+1
+ ENDIF
+ 650 CONTINUE
+ 700 CONTINUE
+ DO 705 I=1,LEV
+ DO 703 J=1,LEV
+ IF (HVALS(I).EQ.IORDER(J)) THEN
+ IORDER(J)=I
+ GOTO 705
+ ENDIF
+ 703 CONTINUE
+ 705 CONTINUE
+!
+ RETURN
+ END SUBROUTINE HCASS
+!-----------------------------------------------------------------------------
+!+++++++++++++++++++++++++++++++++++++++++++++++++C
+! C
+! Construct a DENDROGRAM of the top 8 levels of C
+! a HIERARCHIC CLUSTERING. C
+! C
+! Parameters: C
+! C
+! IORDER, HEIGHT, CRITVAL: vectors of length LEV C
+! defining the dendrogram. C
+! These are: the ordering of objects C
+! along the bottom of the dendrogram C
+! (IORDER); the height of the vertical C
+! above each object, in ordinal values C
+! (HEIGHT); and in real values (CRITVAL).C
+! C
+! NOTE: these vectors MUST have been set up with C
+! LEV = 9 in the prior call to routine C
+! HCASS.
+! C
+! F. Murtagh, ESA/ESO/STECF, Garching, Feb. 1986.C
+! C
+!-------------------------------------------------C
+ SUBROUTINE HCDEN(LEV,IORDER,HEIGHT,CRITVAL)
+! include 'COMMON.IOUNITS'
+ integer :: LEV
+ CHARACTER(len=80) :: LINE
+ INTEGER :: IORDER(LEV),HEIGHT(LEV)
+ REAL(kind=4) :: CRITVAL(LEV)
+! INTEGER OUT(3*LEV,3*LEV)
+! INTEGER UP,ACROSS,BLANK
+ CHARACTER(len=1) :: OUT(3*LEV,3*LEV)
+ CHARACTER(len=1) :: UP,ACROSS,BLANK
+ DATA UP,ACROSS,BLANK /'|','-',' '/
+ integer :: I,I2,J,J2,K,I3,L,IC,IDUM
+!
+!
+ DO I=1,3*LEV
+ DO J=1,3*LEV
+ OUT(I,J)=BLANK
+ ENDDO
+ ENDDO
+!
+!
+ DO I=3,3*LEV,3
+ I2=I/3
+!
+ J2=3*LEV+1-3*HEIGHT(I2)
+ DO J=3*LEV,J2,-1
+ OUT(J,I)=UP
+ ENDDO
+!
+ DO K=I,3,-1
+ I3=INT((K+2)/3)
+ IF ( (3*LEV+1-HEIGHT(I3)*3).LT.J2) GOTO 100
+ OUT(J2,K)=ACROSS
+ ENDDO
+ 100 CONTINUE
+!
+ ENDDO
+!
+!
+ IC=3
+ DO I=1,3*LEV
+ IF (I.EQ.IC+1) THEN
+ IDUM=IC/3
+ IDUM=LEV-IDUM
+ DO L=1,LEV
+ IF (HEIGHT(L).EQ.IDUM) GOTO 190
+ ENDDO
+ 190 IDUM=L
+ WRITE(iout,200) CRITVAL(IDUM),(OUT(I,J),J=1,3*LEV)
+ IC=IC+3
+ ELSE
+ LINE = ' '
+ WRITE(iout,210) (OUT(I,J),J=1,3*LEV)
+ ENDIF
+ 200 FORMAT(1H ,8X,F12.2,4X,27000A1)
+ 210 FORMAT(1H ,24X,27000A1)
+ ENDDO
+ WRITE(iout,250)
+ WRITE(iout,220)(IORDER(J),J=1,LEV)
+ WRITE(iout,250)
+ 220 FORMAT(1H ,24X,9000I3)
+ WRITE(iout,230) LEV
+ 230 FORMAT(1H ,13X,'CRITERION CLUSTERS 1 TO ',i3)
+ WRITE(iout,240) LEV-1
+ 240 FORMAT(1H ,13X,'VALUES. (TOP ',i3,' LEVELS OF HIERARCHY).')
+ 250 FORMAT(/)
+!
+!
+ RETURN
+ END SUBROUTINE HCDEN
+!-----------------------------------------------------------------------------
+ end module hc_
+!-----------------------------------------------------------------------------
+!-----------------------------------------------------------------------------