#TODO: docstring. Group according to the distance value provided by
        ``distInfo['eucDist']`` with the limitation that each ... category value
        can occur only once per group.

    :param matchArr: #TODO: docstring
    :param distInfo: #TODO: docstring, must be sorted, provide keys "posPairs"
        and "eucDist". As generated by :func:`calcDistMatchArr()`
    :param distLimit: #TODO: docstring
    :param categoryKey: #TODO: docstring

    :returns: #TODO: docstring

def proximityGrouping(matchArr, distInfo, distLimit, categoryKey):
    """ #TODO: docstring. Group according to the distance value provided by
        ``distInfo['eucDist']`` with the limitation that each ... category value
        can occur only once per group.

    :param matchArr: #TODO: docstring
    :param distInfo: #TODO: docstring, must be sorted, provide keys "posPairs"
        and "eucDist". As generated by :func:`calcDistMatchArr()`
    :param distLimit: #TODO: docstring
    :param categoryKey: #TODO: docstring

    :returns: #TODO: docstring
    """
    #Group fi according to their proximity
    matchArrSize = listvalues(matchArr)[0].size

    linkageGroups = {p: [p] for p in range(matchArrSize)}
    posToGroup = {p: p for p in range(matchArrSize)}
    groupCategories = {p: set([s]) for p, s in zip(range(matchArrSize),
                                                  matchArr[categoryKey]
                                                  )
                      }
    for (pos1, pos2), dist in zip(distInfo['posPairs'], distInfo['eucDist']):
        if dist > distLimit:
            break

        id1 = posToGroup[pos1]
        id2 = posToGroup[pos2]
        if groupCategories[id1].intersection(groupCategories[id2]):
            continue

        linkageGroups[id1].extend(linkageGroups[id2])
        groupCategories[id1].update(groupCategories[id2])
        for _pos in linkageGroups[id2]:
            posToGroup[_pos] = id1
        del linkageGroups[id2]
        del groupCategories[id2]

    return linkageGroups