Beispiel #1
0
class DistanceBand(W):
    """
    Spatial weights based on distance band.

    Parameters
    ----------

    data        : array
                  (n,k) or KDTree where KDtree.data is array (n,k)
                  n observations on k characteristics used to measure
                  distances between the n objects
    threshold  : float
                 distance band
    p          : float
                 Minkowski p-norm distance metric parameter:
                 1<=p<=infinity
                 2: Euclidean distance
                 1: Manhattan distance
    binary     : boolean
                 If true w_{ij}=1 if d_{i,j}<=threshold, otherwise w_{i,j}=0
                 If false wij=dij^{alpha}
    alpha      : float
                 distance decay parameter for weight (default -1.0)
                 if alpha is positive the weights will not decline with
                 distance. If binary is True, alpha is ignored

    ids         : list
                  values to use for keys of the neighbors and weights dicts

    Attributes
    ----------
    weights : dict
              of neighbor weights keyed by observation id

    neighbors : dict
                of neighbors keyed by observation id

    Examples
    --------

    >>> points=[(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)]
    >>> wcheck = pysal.W({0: [1, 3], 1: [0, 3], 2: [], 3: [0, 1], 4: [5], 5: [4]})
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w=DistanceBand(points,threshold=11.2)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> pysal.weights.util.neighbor_equality(w, wcheck)
    True
    >>> w=DistanceBand(points,threshold=14.2)
    >>> wcheck = pysal.W({0: [1, 3], 1: [0, 3, 4], 2: [4], 3: [1, 0], 4: [5, 2, 1], 5: [4]})
    >>> pysal.weights.util.neighbor_equality(w, wcheck)
    True



    inverse distance weights

    >>> w=DistanceBand(points,threshold=11.2,binary=False)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w.weights[0]
    [0.10000000000000001, 0.089442719099991588]
    >>> w.neighbors[0]
    [1, 3]
    >>>

    gravity weights

    >>> w=DistanceBand(points,threshold=11.2,binary=False,alpha=-2.)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w.weights[0]
    [0.01, 0.0079999999999999984]

    Notes
    -----

    This was initially implemented running scipy 0.8.0dev (in epd 6.1).
    earlier versions of scipy (0.7.0) have a logic bug in scipy/sparse/dok.py
    so serge changed line 221 of that file on sal-dev to fix the logic bug.

    """
    def __init__(self,
                 data,
                 threshold,
                 p=2,
                 alpha=-1.0,
                 binary=True,
                 ids=None):
        """Casting to floats is a work around for a bug in scipy.spatial.
        See detail in pysal issue #126.

        """
        if isKDTree(data):
            self.kd = data
            self.data = self.kd.data
        else:
            try:
                data = np.asarray(data)
                if data.dtype.kind != 'f':
                    data = data.astype(float)
                self.data = data
                self.kd = KDTree(self.data)
            except:
                raise ValueError("Could not make array from data")

        self.p = p
        self.threshold = threshold
        self.binary = binary
        self.alpha = alpha
        self._band()
        neighbors, weights = self._distance_to_W(ids)
        W.__init__(self, neighbors, weights, ids)

    def _band(self):
        """Find all pairs within threshold.

        """
        self.dmat = self.kd.sparse_distance_matrix(self.kd,
                                                   max_distance=self.threshold)

    def _distance_to_W(self, ids=None):
        if ids:
            ids = np.array(ids)
        else:
            ids = np.arange(self.dmat.shape[0])
        neighbors = dict([(i, []) for i in ids])
        weights = dict([(i, []) for i in ids])
        if self.binary:
            for key, weight in self.dmat.items():
                i, j = key
                if i != j:
                    if j not in neighbors[i]:
                        weights[i].append(1)
                        neighbors[i].append(j)
                    if i not in neighbors[j]:
                        weights[j].append(1)
                        neighbors[j].append(i)

        else:
            for key, weight in self.dmat.items():
                i, j = key
                if i != j:
                    if j not in neighbors[i]:
                        weights[i].append(weight**self.alpha)
                        neighbors[i].append(j)
                    if i not in neighbors[j]:
                        weights[j].append(weight**self.alpha)
                        neighbors[j].append(i)

        return neighbors, weights
Beispiel #2
0
class DistanceBand(W):
    """
    Spatial weights based on distance band.

    Parameters
    ----------

    data        : array
                  (n,k) or KDTree where KDtree.data is array (n,k)
                  n observations on k characteristics used to measure
                  distances between the n objects
    threshold  : float
                 distance band
    p          : float
                 Minkowski p-norm distance metric parameter:
                 1<=p<=infinity
                 2: Euclidean distance
                 1: Manhattan distance
    binary     : boolean
                 If true w_{ij}=1 if d_{i,j}<=threshold, otherwise w_{i,j}=0
                 If false wij=dij^{alpha}
    alpha      : float
                 distance decay parameter for weight (default -1.0)
                 if alpha is positive the weights will not decline with
                 distance. If binary is True, alpha is ignored

    ids         : list
                  values to use for keys of the neighbors and weights dicts
    
    build_sp    : boolean
                  True to build sparse distance matrix and false to build dense
                  distance matrix; significant speed gains may be obtained
                  dending on the sparsity of the of distance_matrix and
                  threshold that is applied
    silent      : boolean
                  By default PySAL will print a warning if the
                  dataset contains any disconnected observations or
                  islands. To silence this warning set this
                  parameter to True.

    Attributes
    ----------
    weights : dict
              of neighbor weights keyed by observation id

    neighbors : dict
                of neighbors keyed by observation id

    Examples
    --------

    >>> points=[(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)]
    >>> wcheck = pysal.W({0: [1, 3], 1: [0, 3], 2: [], 3: [0, 1], 4: [5], 5: [4]})
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w=DistanceBand(points,threshold=11.2)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> pysal.weights.util.neighbor_equality(w, wcheck)
    True
    >>> w=DistanceBand(points,threshold=14.2)
    >>> wcheck = pysal.W({0: [1, 3], 1: [0, 3, 4], 2: [4], 3: [1, 0], 4: [5, 2, 1], 5: [4]})
    >>> pysal.weights.util.neighbor_equality(w, wcheck)
    True



    inverse distance weights

    >>> w=DistanceBand(points,threshold=11.2,binary=False)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w.weights[0]
    [0.10000000000000001, 0.089442719099991588]
    >>> w.neighbors[0]
    [1, 3]
    >>>

    gravity weights

    >>> w=DistanceBand(points,threshold=11.2,binary=False,alpha=-2.)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w.weights[0]
    [0.01, 0.0079999999999999984]

    Notes
    -----

    This was initially implemented running scipy 0.8.0dev (in epd 6.1).
    earlier versions of scipy (0.7.0) have a logic bug in scipy/sparse/dok.py
    so serge changed line 221 of that file on sal-dev to fix the logic bug.

    """

    def __init__(self, data, threshold, p=2, alpha=-1.0, binary=True, ids=None,
            build_sp=True, silent=False):
        """Casting to floats is a work around for a bug in scipy.spatial.
        See detail in pysal issue #126.

        """
        self.p = p
        self.threshold = threshold
        self.binary = binary
        self.alpha = alpha
        self.build_sp = build_sp
        self.silent = silent
        
        if isKDTree(data):
            self.kd = data
            self.data = self.kd.data
        else:
            if self.build_sp:
                try:
                    data = np.asarray(data)
                    if data.dtype.kind != 'f':
                        data = data.astype(float)
                    self.data = data
                    self.kd = KDTree(self.data)
                except:
                    raise ValueError("Could not make array from data")        
            else:
                self.data = data
                self.kd = None       
        self._band()
        neighbors, weights = self._distance_to_W(ids)
        W.__init__(self, neighbors, weights, ids, silent_island_warning=self.silent)

    @classmethod
    def from_shapefile(cls, filepath, threshold, idVariable=None, **kwargs):
        """
        Distance-band based weights from shapefile

        Arguments
        ---------
        shapefile   : string
                      shapefile name with shp suffix
        idVariable  : string
                      name of column in shapefile's DBF to use for ids

        Returns
        --------
        Kernel Weights Object

        See Also
        ---------
        :class: `pysal.weights.DistanceBand`
        :class: `pysal.weights.W`
        """
        points = get_points_array_from_shapefile(filepath)
        if idVariable is not None:
            ids = get_ids(filepath, idVariable)
        else:
            ids = None
        return cls.from_array(points, threshold, ids=ids, **kwargs)
    
    @classmethod
    def from_array(cls, array, threshold, **kwargs):
        """
        Construct a DistanceBand weights from an array. Supports all the same options
        as :class:`pysal.weights.DistanceBand`

        See Also
        --------
        :class:`pysal.weights.DistanceBand`
        :class:`pysal.weights.W`
        """
        return cls(array, threshold, **kwargs)
    
    @classmethod
    def from_dataframe(cls, df, threshold, geom_col='geometry', ids=None, **kwargs):
        """
        Make DistanceBand weights from a dataframe.

        Parameters
        ----------
        df      :   pandas.dataframe
                    a dataframe with a geometry column that can be used to
                    construct a W object
        geom_col :   string
                    column name of the geometry stored in df
        ids     :   string or iterable
                    if string, the column name of the indices from the dataframe
                    if iterable, a list of ids to use for the W
                    if None, df.index is used.

        See Also
        --------
        :class:`pysal.weights.DistanceBand`
        :class:`pysal.weights.W`
        """
        pts = get_points_array(df[geom_col])
        if ids is None:
            ids = df.index.tolist()
        elif isinstance(ids, str):
            ids = df[ids].tolist()
        return cls(pts, threshold, ids=ids, **kwargs)

    def _band(self):
        """Find all pairs within threshold.

        """
        if self.build_sp:    
            self.dmat = self.kd.sparse_distance_matrix(
                    self.kd, max_distance=self.threshold).tocsr()
        else:
            if str(self.kd).split('.')[-1][0:10] == 'Arc_KDTree':
            	raise TypeError('Unable to calculate dense arc distance matrix;'
            	        ' parameter "build_sp" must be set to True for arc'
            	        ' distance type weight')
            self.dmat = self._spdistance_matrix(self.data, self.data, self.threshold)


    def _distance_to_W(self, ids=None):
        if self.binary:
            self.dmat[self.dmat>0] = 1
            self.dmat.eliminate_zeros()
            tempW = WSP2W(WSP(self.dmat), silent_island_warning=self.silent)
            neighbors = tempW.neighbors
            weight_keys = tempW.weights.keys()
            weight_vals = tempW.weights.values()
            weights = dict(zip(weight_keys, map(list, weight_vals)))
            return neighbors, weights
        else:
            weighted = self.dmat.power(self.alpha)
            weighted[weighted==np.inf] = 0
            weighted.eliminate_zeros()
            tempW = WSP2W(WSP(weighted), silent_island_warning=self.silent)
            neighbors = tempW.neighbors
            weight_keys = tempW.weights.keys()
            weight_vals = tempW.weights.values()
            weights = dict(zip(weight_keys, map(list, weight_vals)))
            return neighbors, weights

    def _spdistance_matrix(self, x,y, threshold=None):
        dist = distance_matrix(x,y)
        if threshold is not None:
            zeros = dist > threshold
            dist[zeros] = 0
        return sp.csr_matrix(dist)
Beispiel #3
0
class DistanceBand(W):
    """
    Spatial weights based on distance band.

    Parameters
    ----------

    data        : array
                  (n,k) or KDTree where KDtree.data is array (n,k)
                  n observations on k characteristics used to measure
                  distances between the n objects
    threshold  : float
                 distance band
    p          : float
                 Minkowski p-norm distance metric parameter:
                 1<=p<=infinity
                 2: Euclidean distance
                 1: Manhattan distance
    binary     : boolean
                 If true w_{ij}=1 if d_{i,j}<=threshold, otherwise w_{i,j}=0
                 If false wij=dij^{alpha}
    alpha      : float
                 distance decay parameter for weight (default -1.0)
                 if alpha is positive the weights will not decline with
                 distance. If binary is True, alpha is ignored

    ids         : list
                  values to use for keys of the neighbors and weights dicts
    
    build_sp    : boolean
                  True to build sparse distance matrix and false to build dense
                  distance matrix; significant speed gains may be obtained
                  dending on the sparsity of the of distance_matrix and
                  threshold that is applied
    silent      : boolean
                  By default PySAL will print a warning if the
                  dataset contains any disconnected observations or
                  islands. To silence this warning set this
                  parameter to True.

    Attributes
    ----------
    weights : dict
              of neighbor weights keyed by observation id

    neighbors : dict
                of neighbors keyed by observation id

    Examples
    --------

    >>> points=[(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)]
    >>> wcheck = pysal.W({0: [1, 3], 1: [0, 3], 2: [], 3: [0, 1], 4: [5], 5: [4]})
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w=DistanceBand(points,threshold=11.2)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> pysal.weights.util.neighbor_equality(w, wcheck)
    True
    >>> w=DistanceBand(points,threshold=14.2)
    >>> wcheck = pysal.W({0: [1, 3], 1: [0, 3, 4], 2: [4], 3: [1, 0], 4: [5, 2, 1], 5: [4]})
    >>> pysal.weights.util.neighbor_equality(w, wcheck)
    True



    inverse distance weights

    >>> w=DistanceBand(points,threshold=11.2,binary=False)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w.weights[0]
    [0.10000000000000001, 0.089442719099991588]
    >>> w.neighbors[0]
    [1, 3]
    >>>

    gravity weights

    >>> w=DistanceBand(points,threshold=11.2,binary=False,alpha=-2.)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w.weights[0]
    [0.01, 0.0079999999999999984]

    Notes
    -----

    This was initially implemented running scipy 0.8.0dev (in epd 6.1).
    earlier versions of scipy (0.7.0) have a logic bug in scipy/sparse/dok.py
    so serge changed line 221 of that file on sal-dev to fix the logic bug.

    """
    def __init__(self,
                 data,
                 threshold,
                 p=2,
                 alpha=-1.0,
                 binary=True,
                 ids=None,
                 build_sp=True,
                 silent=False):
        """Casting to floats is a work around for a bug in scipy.spatial.
        See detail in pysal issue #126.

        """
        if ids is not None:
            ids = list(ids)
        self.p = p
        self.threshold = threshold
        self.binary = binary
        self.alpha = alpha
        self.build_sp = build_sp
        self.silent = silent

        if isKDTree(data):
            self.kd = data
            self.data = self.kd.data
        else:
            if self.build_sp:
                try:
                    data = np.asarray(data)
                    if data.dtype.kind != 'f':
                        data = data.astype(float)
                    self.data = data
                    self.kd = KDTree(self.data)
                except:
                    raise ValueError("Could not make array from data")
            else:
                self.data = data
                self.kd = None
        self._band()
        neighbors, weights = self._distance_to_W(ids)
        W.__init__(self,
                   neighbors,
                   weights,
                   ids,
                   silent_island_warning=self.silent)

    @classmethod
    def from_shapefile(cls, filepath, threshold, idVariable=None, **kwargs):
        """
        Distance-band based weights from shapefile

        Arguments
        ---------
        shapefile   : string
                      shapefile name with shp suffix
        idVariable  : string
                      name of column in shapefile's DBF to use for ids

        Returns
        --------
        Kernel Weights Object

        See Also
        ---------
        :class: `pysal.weights.DistanceBand`
        :class: `pysal.weights.W`
        """
        points = get_points_array_from_shapefile(filepath)
        if idVariable is not None:
            ids = get_ids(filepath, idVariable)
        else:
            ids = None
        return cls.from_array(points, threshold, ids=ids, **kwargs)

    @classmethod
    def from_array(cls, array, threshold, **kwargs):
        """
        Construct a DistanceBand weights from an array. Supports all the same options
        as :class:`pysal.weights.DistanceBand`

        See Also
        --------
        :class:`pysal.weights.DistanceBand`
        :class:`pysal.weights.W`
        """
        return cls(array, threshold, **kwargs)

    @classmethod
    def from_dataframe(cls,
                       df,
                       threshold,
                       geom_col='geometry',
                       ids=None,
                       **kwargs):
        """
        Make DistanceBand weights from a dataframe.

        Parameters
        ----------
        df      :   pandas.dataframe
                    a dataframe with a geometry column that can be used to
                    construct a W object
        geom_col :   string
                    column name of the geometry stored in df
        ids     :   string or iterable
                    if string, the column name of the indices from the dataframe
                    if iterable, a list of ids to use for the W
                    if None, df.index is used.

        See Also
        --------
        :class:`pysal.weights.DistanceBand`
        :class:`pysal.weights.W`
        """
        pts = get_points_array(df[geom_col])
        if ids is None:
            ids = df.index.tolist()
        elif isinstance(ids, str):
            ids = df[ids].tolist()
        else:
            ids = df.index.tolist()
        return cls(pts, threshold, ids=ids, **kwargs)

    def _band(self):
        """Find all pairs within threshold.

        """
        if self.build_sp:
            self.dmat = self.kd.sparse_distance_matrix(
                self.kd, max_distance=self.threshold, p=self.p).tocsr()
        else:
            if str(self.kd).split('.')[-1][0:10] == 'Arc_KDTree':
                raise TypeError(
                    'Unable to calculate dense arc distance matrix;'
                    ' parameter "build_sp" must be set to True for arc'
                    ' distance type weight')
            self.dmat = self._spdistance_matrix(self.data, self.data,
                                                self.threshold)

    def _distance_to_W(self, ids=None):
        if self.binary:
            self.dmat[self.dmat > 0] = 1
            self.dmat.eliminate_zeros()
            tempW = WSP2W(WSP(self.dmat, id_order=ids),
                          silent_island_warning=self.silent)
            neighbors = tempW.neighbors
            weight_keys = list(tempW.weights.keys())
            weight_vals = list(tempW.weights.values())
            weights = dict(list(zip(weight_keys, list(map(list,
                                                          weight_vals)))))
            return neighbors, weights
        else:
            weighted = self.dmat.power(self.alpha)
            weighted[weighted == np.inf] = 0
            weighted.eliminate_zeros()
            tempW = WSP2W(WSP(weighted, id_order=ids),
                          silent_island_warning=self.silent)
            neighbors = tempW.neighbors
            weight_keys = list(tempW.weights.keys())
            weight_vals = list(tempW.weights.values())
            weights = dict(list(zip(weight_keys, list(map(list,
                                                          weight_vals)))))
            return neighbors, weights

    def _spdistance_matrix(self, x, y, threshold=None):
        dist = distance_matrix(x, y)
        if threshold is not None:
            zeros = dist > threshold
            dist[zeros] = 0
        return sp.csr_matrix(dist)
Beispiel #4
0
class DistanceBand(W):
    """Spatial weights based on distance band

    Parameters
    ----------

    data        : array (n,k) or KDTree where KDtree.data is array (n,k)
                  n observations on k characteristics used to measure
                  distances between the n objects
    threshold  : float
                 distance band
    p          : float
                 Minkowski p-norm distance metric parameter:
                 1<=p<=infinity
                 2: Euclidean distance
                 1: Manhattan distance
    binary     : binary
                 If true w_{ij}=1 if d_{i,j}<=threshold, otherwise w_{i,j}=0
                 If false wij=dij^{alpha}
    alpha      : float
                 distance decay parameter for weight (default -1.0)
                 if alpha is positive the weights will not decline with
                 distance. If binary is True, alpha is ignored

    Examples
    --------

    >>> points=[(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)]
    >>> w=DistanceBand(points,threshold=11.2)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w.weights
    {0: [1, 1], 1: [1, 1], 2: [], 3: [1, 1], 4: [1], 5: [1]}
    >>> w.neighbors
    {0: [1, 3], 1: [0, 3], 2: [], 3: [0, 1], 4: [5], 5: [4]}
    >>> w=DistanceBand(points,threshold=14.2)
    >>> w.weights
    {0: [1, 1], 1: [1, 1, 1], 2: [1], 3: [1, 1], 4: [1, 1, 1], 5: [1]}
    >>> w.neighbors
    {0: [1, 3], 1: [0, 3, 4], 2: [4], 3: [0, 1], 4: [1, 2, 5], 5: [4]}

    inverse distance weights

    >>> w=DistanceBand(points,threshold=11.2,binary=False)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w.weights[0]
    [0.10000000000000001, 0.089442719099991588]
    >>> w.neighbors[0]
    [1, 3]
    >>>

    gravity weights

    >>> w=DistanceBand(points,threshold=11.2,binary=False,alpha=-2.)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w.weights[0]
    [0.01, 0.0079999999999999984]

    Notes
    -----

    this was initially implemented running scipy 0.8.0dev (in epd 6.1).
    earlier versions of scipy (0.7.0) have a logic bug in scipy/sparse/dok.py
    so serge changed line 221 of that file on sal-dev to fix the logic bug

    """
    def __init__(self, data, threshold, p=2, alpha=-1.0, binary=True, ids=None):
        """
        Casting to floats is a work around for a bug in scipy.spatial.  See detail in pysal issue #126
        """
        if issubclass(type(data), scipy.spatial.KDTree):
            self.kd = data
            self.data = self.kd.data
        else:
            try:
                data = np.asarray(data)
                if data.dtype.kind != 'f':
                    data = data.astype(float)
                self.data = data
                self.kd = KDTree(self.data)
            except:
                raise ValueError("Could not make array from data")

        self.p = p
        self.threshold = threshold
        self.binary = binary
        self.alpha = alpha
        self._band()
        neighbors, weights = self._distance_to_W(ids)
        W.__init__(self, neighbors, weights, ids)

    def _band(self):
        """
        find all pairs within threshold
        """
        kd = self.kd
        #ns=[kd.query_ball_point(point,self.threshold) for point in self.data]
        ns = kd.query_ball_tree(kd, self.threshold)
        self._nmat = ns

    def _distance_to_W(self, ids=None):
        allneighbors = {}
        weights = {}
        if ids:
            ids = np.array(ids)
        else:
            ids = np.arange(len(self._nmat))
        if self.binary:
            for i, neighbors in enumerate(self._nmat):
                ns = [ni for ni in neighbors if ni != i]
                neigh = list(ids[ns])
                if len(neigh) == 0:
                    allneighbors[ids[i]] = []
                    weights[ids[i]] = []
                else:
                    allneighbors[ids[i]] = neigh
                    weights[ids[i]] = [1] * len(ns)
        else:
            self.dmat = self.kd.sparse_distance_matrix(
                self.kd, max_distance=self.threshold)
            for i, neighbors in enumerate(self._nmat):
                ns = [ni for ni in neighbors if ni != i]
                neigh = list(ids[ns])
                if len(neigh) == 0:
                    allneighbors[ids[i]] = []
                    weights[ids[i]] = []
                else:
                    try:
                        allneighbors[ids[i]] = neigh
                        weights[ids[i]] = [self.dmat[(
                            i, j)] ** self.alpha for j in ns]
                    except ZeroDivisionError, e:
                        print(e, "Cannot compute inverse distance for elements at same location (distance=0).")
        return allneighbors, weights
Beispiel #5
0
class DistanceBand(W):
    """
    Spatial weights based on distance band.

    Parameters
    ----------

    data        : array
                  (n,k) or KDTree where KDtree.data is array (n,k)
                  n observations on k characteristics used to measure
                  distances between the n objects
    threshold  : float
                 distance band
    p          : float
                 Minkowski p-norm distance metric parameter:
                 1<=p<=infinity
                 2: Euclidean distance
                 1: Manhattan distance
    binary     : boolean
                 If true w_{ij}=1 if d_{i,j}<=threshold, otherwise w_{i,j}=0
                 If false wij=dij^{alpha}
    alpha      : float
                 distance decay parameter for weight (default -1.0)
                 if alpha is positive the weights will not decline with
                 distance. If binary is True, alpha is ignored

    ids         : list
                  values to use for keys of the neighbors and weights dicts

    Examples
    --------

    >>> points=[(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)]
    >>> w=DistanceBand(points,threshold=11.2)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w.weights
    {0: [1, 1], 1: [1, 1], 2: [], 3: [1, 1], 4: [1], 5: [1]}
    >>> w.neighbors
    {0: [1, 3], 1: [0, 3], 2: [], 3: [1, 0], 4: [5], 5: [4]}
    >>> w=DistanceBand(points,threshold=14.2)
    >>> w.weights
    {0: [1, 1], 1: [1, 1, 1], 2: [1], 3: [1, 1], 4: [1, 1, 1], 5: [1]}
    >>> w.neighbors
    {0: [1, 3], 1: [0, 3, 4], 2: [4], 3: [1, 0], 4: [5, 1, 2], 5: [4]}

    inverse distance weights

    >>> w=DistanceBand(points,threshold=11.2,binary=False)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w.weights[0]
    [0.10000000000000001, 0.089442719099991588]
    >>> w.neighbors[0]
    [1, 3]
    >>>

    gravity weights

    >>> w=DistanceBand(points,threshold=11.2,binary=False,alpha=-2.)
    WARNING: there is one disconnected observation (no neighbors)
    Island id:  [2]
    >>> w.weights[0]
    [0.01, 0.0079999999999999984]

    Notes
    -----

    This was initially implemented running scipy 0.8.0dev (in epd 6.1).
    earlier versions of scipy (0.7.0) have a logic bug in scipy/sparse/dok.py
    so serge changed line 221 of that file on sal-dev to fix the logic bug.

    """

    def __init__(self, data, threshold, p=2, alpha=-1.0, binary=True, ids=None):
        """Casting to floats is a work around for a bug in scipy.spatial.
        See detail in pysal issue #126.

        """
        if issubclass(type(data), scipy.spatial.KDTree):
            self.kd = data
            self.data = self.kd.data
        else:
            try:
                data = np.asarray(data)
                if data.dtype.kind != 'f':
                    data = data.astype(float)
                self.data = data
                self.kd = KDTree(self.data)
            except:
                raise ValueError("Could not make array from data")

        self.p = p
        self.threshold = threshold
        self.binary = binary
        self.alpha = alpha
        self._band()
        neighbors, weights = self._distance_to_W(ids)
        W.__init__(self, neighbors, weights, ids)

    def _band(self):
        """Find all pairs within threshold.

        """
        self.dmat = self.kd.sparse_distance_matrix(
                self.kd, max_distance=self.threshold)

    def _distance_to_W(self, ids=None):
        if ids:
            ids = np.array(ids)
        else:
            ids = np.arange(self.dmat.shape[0])
        neighbors = dict([(i,[]) for i in ids])
        weights = dict([(i,[]) for i in ids])
        if self.binary:
            for key,weight in self.dmat.items():
                i,j = key
                if j not in neighbors[i]:
                    weights[i].append(1)
                    neighbors[i].append(j)
                if i not in neighbors[j]:
                    weights[j].append(1)
                    neighbors[j].append(i)

        else:
            for key,weight in self.dmat.items():
                i,j = key
                if j not in neighbors[i]:
                    weights[i].append(weight**self.alpha)
                    neighbors[i].append(j)
                if i not in neighbors[j]:
                    weights[j].append(weight**self.alpha)
                    neighbors[j].append(i)

        return neighbors, weights