예제 #1
0
def run(ngramPath, sid_seq, outPath):
    """
    this function is a simpler version of the main function
    it is a wrapper around runDiana
    intended to be used for rhcBootstrap (my testing script)
    :type ngramPath: str
          - the path to the computed pattern dataset
    :type sid_seq: Dict{int:Dict{str:int}}
          - for each user, record the pattern and corresponding occurence #
    :type outPath: str
          - the path to store the output and temporary file
    """
    global matrixCompTotal

    startTime = time.time()

    idxToSid = [x+1 for x in range(len(sid_seq))]

    idfMap = rhc.excludeFeatures(rhc.getIdf(sid_seq, idxToSid), [])

    matrix = calculateDistance.partialMatrix(
        idxToSid, idfMap, ngramPath, 'tmp_%sroot' % int(time.time()),
        outPath, True)

    print('[LOG]: first matrixTime %f' % (time.time() - startTime))
    matrixCompTotal += time.time() - startTime

    hc = HCClustering(
        matrix, sid_seq, outPath, [], idxToSid,
        sizeThreshold=0.05 * len(sid_seq), idfMap=idfMap)
    result = hc.runDiana()

    print('[STAT]: total clustering time %f' % (time.time() - startTime))
    return result
예제 #2
0
    def runDiana(self):
        """
        Perform recursive hierarchical clustering
        """
        global matrixCompTotal, splitTotal, modularityTotal, diaTotal
        global excluTotal

        M = self.matrix
        self.modularityBasics()
        print('[LOG]: finished calculating modularityBasics')

        # child Cid => parent Cid
        clusterHi = []

        # record the evaluation metrics
        evalResults = {}

        cid = 1
        clusters = [(range(len(self.matrix)), self.maxDistance, cid)]

        # get a mapping from cid => list of row sums for all ids in cluster
        self.sumEntriesMap = {}
        self.sumEntriesMap[cid] = np.sum(self.matrix, axis=1, dtype=np.float64)

        while clusters[-1][1] and len(clusters[-1][0]) > self.sizeThreshold:
            parentCid = clusters[-1][2]
            # print('splitting %s\t%s' % (clusters[-1][1],clusters[-1][2]))
            clusterHi.append((parentCid, cid + 1, cid + 2))

            curTime = time.time()
            (clusterA, clusterB, sumEntryA, sumEntryB, sumAB) = \
                (self.splitCluster(clusters.pop()))
            splitTotal += time.time() - curTime

            curTime = time.time()
            cid += 1
            self.sumEntriesMap[cid] = sumEntryA
            clusters.append((clusterA, self.getDia(cid, clusterA), cid))
            # clusters.append((clusterA, np.mean(sumEntryA) / len(clusterA), cid))
            cid += 1
            self.sumEntriesMap[cid] = sumEntryB
            clusters.append((clusterB, self.getDia(cid, clusterB), cid))
            # clusters.append((clusterB, np.mean(sumEntryB) / len(clusterB), cid))
            diaTotal += time.time() - curTime

            curTime = time.time()
            clusters = \
                sorted(clusters, key=lambda x: (x[1], len(x[0]))
                       if len(x[0]) > self.sizeThreshold else (0, 0))
            if len(clusters) == 2:
                # if it is the first time to compute modularity
                evalResult = self.evaluateModularity(
                    (clusterA, clusterB), (sumEntryA, sumEntryB))
            else:
                # if it is based on the previous scores
                evalResult = evalResults[len(clusters) - 1] + \
                    self.evaluateModularityShift((clusterA, clusterB), sumAB)
            modularityTotal += time.time() - curTime

            # print(sorted([len(x[0]) for x in clusters], reverse = True))
            # print(len(clusters[-1][0]))
            evalResults[len(clusters)] = evalResult
            # print('cluster num is %d, modularity %f' % (len(clusters), evalResult))

        # print(evalResults)
        sweetSpot = rhc.getSweetSpot(evalResults, 5)
        sweetSpot = sorted(evalResults.keys(),
                           key=lambda x: abs(x - sweetSpot))[0]
        print('[LOG]: sweetSpot is %d, modularity %f' %
              (sweetSpot, evalResults[sweetSpot]))

        # merge the clusters to the point of sweet spot
        clusterMap = dict([(row[2], row) for row in clusters])
        cids = [(row[2]) for row in clusters]
        while(len(cids) > sweetSpot):
            (parentCid, childACid, childBCid) = clusterHi.pop()
            # dismeter doesn't matter, so put zero here
            clusterMap[parentCid] = \
                (clusterMap[childACid][0] + clusterMap[childBCid][0],
                 0, parentCid)
            cids.append(parentCid)
            cids.remove(childACid)
            cids.remove(childBCid)

        # reconstruct the cluster list after merging
        clusters = [(x[0], x[1], None, x[2]) for cid, x in clusterMap.items()
                    if cid in cids]

        # get the exclusion map according to the current clustering
        startTime = time.time()
        excludeMap, exclusionScoreMap, scoreMap = \
            rhc.getExclusionMap(clusters, self.sid_seq, self.idfMap,
                                self.idxToSid, [row[3] for row in clusters],
                                self.exclusions)
        excluTotal += time.time() - startTime

        # for each cluster, we start a new clustering
        results = []
        for cidx in range(len(clusters)):
            row = clusters[cidx]
            idxs = row[0]    # get the list of all node in clusters
            sids = sorted([self.idxToSid[nidx] for nidx in idxs])
            excludedFeatures = excludeMap[row[3]]
            excludedScores = exclusionScoreMap[row[3]]

            # if we want to continue cluster this subcluster
            if len(sids) > self.sizeThreshold:
                newExclusions = self.exclusions + excludedFeatures
                # remove sids where the vector have all zeros
                newExclusionSet = set(newExclusions)
                oldLen = len(sids)
                excludedSids = [sid for sid in sids if len(
                    set(self.sid_seq[sid].keys()) - newExclusionSet) == 0]
                sids = [sid for sid in sids if len(
                    set(self.sid_seq[sid].keys()) - newExclusionSet) > 0]
                # if the cluster size is too small after feature selection,
                # don't cluster it
                # or if the cluster diameter is 0
                if not len(sids) > self.sizeThreshold:
                    result = ('l', sids + excludedSids,
                              {'exclusions': excludedFeatures,
                               'exclusionsScore': excludedScores})
                else:
                    matrixStart = time.time()
                    matrix = calculateDistance.partialMatrix(
                        sids,
                        rhc.excludeFeatures(rhc.getIdf(sid_seq, sids),
                                            newExclusions),
                        ngramPath,
                        'tmp_%d' % row[3],
                        '%st%d_' % (self.outPath, row[-1]),
                        True)
                    matrixCompTotal += time.time() - matrixStart
                    # after the matrix is calculated, we need to handle a
                    # speacial case where all entries in the matrix is zero,
                    # besically means if the first row of the
                    # matrix adds up to zero
                    # if this is the case, do not split the cluster
                    if np.sum(matrix[0]) == 0:
                        result = ('l', sids + excludedSids,
                                  {'exclusions': excludedFeatures,
                                   'exclusionsScore': excludedScores})
                    else:
                        # now that we have a new distance matrix, go and
                        # do another round of clustering
                        result = HCClustering(
                            matrix,
                            sid_seq,
                            '%sp%d_' % (self.outPath, row[-1]),
                            newExclusions,
                            sids,
                            self.sizeThreshold).runDiana()
                        if len(results) > 2:
                            info = result[2]
                        else:
                            info = {}

                        # put the excluded sids back as a cluster
                        if (len(excludedSids) > 0):
                            result[1].append(('l', excludedSids,
                                              {'isExclude': True}))

                        info['exclusions'] = excludedFeatures
                        info['exclusionsScore'] = excludedScores
                        # base on the score map, calculate the gini coefficient
                        # score map format {cid:[(feature, score)]}
                        # info['gini'] = getGini([x[1] for x in scoreMap[row[3]]])
                        result = (result[0], result[1], info)
            else:
                result = ('l', sids,
                          {'exclusions': excludedFeatures,
                           'exclusionsScore': excludedScores})
            results.append(result)

        return(('t', results, {'sweetspot': evalResults[sweetSpot]}))