Repository: martinfleis/momepy
Pull request #32 (2019-07-11 16:31:41)

API: use unique_ID column instead of index for spatial weights
To simplify some methods and make them more explicit rather than implicit, majority of spatial weights now require being based on unique ID rather than on index. That allows using weights derived from tessellation directly on buildings and vice versa.

codecov[bot]: # [Codecov](https://codecov.io/gh/martinfleis/momepy/pull/32?src=pr&el=h1) Report
> Merging [#32](https://codecov.io/gh/martinfleis/momepy/pull/32?src=pr&el=desc) into [master](https://codecov.io/gh/martinfleis/momepy/commit/5a515fed71769a525a0fc7e4a6b84351fdd4031a?src=pr&el=desc) will **decrease** coverage by `0.19%`.
> The diff coverage is `87.64%`.

[![Impacted file tree graph](https://codecov.io/gh/martinfleis/momepy/pull/32/graphs/tree.svg?width=650&token=i04999UxkK&height=150&src=pr)](https://codecov.io/gh/martinfleis/momepy/pull/32?src=pr&el=tree)

```diff
@@           Coverage Diff            @@
##           master     #32     +/-   ##
========================================
- Coverage    91.6%   91.4%   -0.2%     
========================================
  Files           9       9             
  Lines        2239    2141     -98     
========================================
- Hits         2051    1957     -94     
+ Misses        188     184      -4
```


| [Impacted Files](https://codecov.io/gh/martinfleis/momepy/pull/32?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [momepy/utils.py](https://codecov.io/gh/martinfleis/momepy/pull/32/diff?src=pr&el=tree#diff-bW9tZXB5L3V0aWxzLnB5) | `89.42% <100%> (ø)` | :arrow_up: |
| [momepy/diversity.py](https://codecov.io/gh/martinfleis/momepy/pull/32/diff?src=pr&el=tree#diff-bW9tZXB5L2RpdmVyc2l0eS5weQ==) | `86.41% <84.21%> (+0.56%)` | :arrow_up: |
| [momepy/intensity.py](https://codecov.io/gh/martinfleis/momepy/pull/32/diff?src=pr&el=tree#diff-bW9tZXB5L2ludGVuc2l0eS5weQ==) | `93.24% <87.5%> (+0.38%)` | :arrow_up: |
| [momepy/dimension.py](https://codecov.io/gh/martinfleis/momepy/pull/32/diff?src=pr&el=tree#diff-bW9tZXB5L2RpbWVuc2lvbi5weQ==) | `98.36% <87.5%> (+0.78%)` | :arrow_up: |
| [momepy/distribution.py](https://codecov.io/gh/martinfleis/momepy/pull/32/diff?src=pr&el=tree#diff-bW9tZXB5L2Rpc3RyaWJ1dGlvbi5weQ==) | `94.11% <88.88%> (-1.66%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/martinfleis/momepy/pull/32?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/martinfleis/momepy/pull/32?src=pr&el=footer). Last update [5a515fe...e317d66](https://codecov.io/gh/martinfleis/momepy/pull/32?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/momepy/dimension.py b/momepy/dimension.py
index 42fb8ed..4dd8735 100644
--- a/momepy/dimension.py
+++ b/momepy/dimension.py
@@ -289,7 +289,7 @@ def longest_axis_length(gdf):
     return series
 
 
-def mean_character(gdf, spatial_weights=None, values=None, order=3, rng=None):
+def mean_character(gdf, values, spatial_weights, unique_id, rng=None):
     """
     Calculates the mean of a character within k steps of morphological tessellation
 
@@ -302,13 +302,12 @@ def mean_character(gdf, spatial_weights=None, values=None, order=3, rng=None):
     ----------
     gdf : GeoDataFrame
         GeoDataFrame containing morphological tessellation
+    values : str, list, np.array, pd.Series
+        the name of the dataframe column, np.array, or pd.Series where is stored character value.
+    unique_id : str
+        name of the column with unique id used as spatial_weights index.
     spatial_weights : libpysal.weights, optional
-        spatial weights matrix - If None, Queen contiguity matrix of set order will be calculated
-        based on objects.
-    values : str, list, np.array, pd.Series (default None)
-        the name of the dataframe column, np.array, or pd.Series where is stored character value. If set to None, function will use area.
-    order : int
-        order of Queen contiguity
+        spatial weights matrix
     rng : Two-element sequence containing floats in range of [0,100], optional
         Percentiles over which to compute the range. Each must be
         between 0 and 100, inclusive. The order of the elements is not important.
@@ -325,16 +324,8 @@ def mean_character(gdf, spatial_weights=None, values=None, order=3, rng=None):
 
     Examples
     --------
-    >>> tessellation['mesh'] = momepy.mean_character(tessellation)
-    Calculating mean character value...
-    Generating weights matrix (Queen) of 3 topological steps...
-    100%|██████████| 144/144 [00:00<00:00, 900.83it/s]
-    Mean character value calculated.
-    >>> tessellation.mesh[0]
-    2922.957260196682
-
-    >>> sw = libpysal.weights.DistanceBand.from_dataframe(tessellation, threshold=100, silence_warnings=True)
-    >>> tessellation['mesh_100'] = momepy.mean_character(tessellation, spatial_weights=sw, values='area')
+    >>> sw = libpysal.weights.DistanceBand.from_dataframe(tessellation, threshold=100, silence_warnings=True, ids='uID')
+    >>> tessellation['mesh_100'] = momepy.mean_character(tessellation, values='area', spatial_weights=sw, unique_id='uID')
     Calculating mean character value...
     100%|██████████| 144/144 [00:00<00:00, 1433.32it/s]
     Mean character value calculated.
@@ -347,39 +338,21 @@ def mean_character(gdf, spatial_weights=None, values=None, order=3, rng=None):
     print('Calculating mean character value...')
     gdf = gdf.copy()
 
-    if spatial_weights is None:
-        print('Generating weights matrix (Queen) of {} topological steps...'.format(order))
-        from momepy import Queen_higher
-        # matrix to define area of analysis (more steps)
-        spatial_weights = Queen_higher(k=order, geodataframe=gdf)
-    else:
-        if not all(gdf.index == range(len(gdf))):
-            raise ValueError('Index is not consecutive range 0:x, spatial weights will not match objects.')
-
     if values is not None:
         if not isinstance(values, str):
             gdf['mm_v'] = values
             values = 'mm_v'
 
     for index, row in tqdm(gdf.iterrows(), total=gdf.shape[0]):
-        neighbours = spatial_weights.neighbors[index]
-        values_list = None
-        if values is not None:
-            values_list = gdf.iloc[neighbours][values].tolist()
-            if values_list:
-                values_list.append(row[values])
-            else:
-                values_list = [row[values]]
-        else:
-            values_list = gdf.iloc[neighbours].geometry.area.tolist()
-            if values_list:
-                values_list.append(row.geometry.area)
-            else:
-                values_list = [row.geometry.area]
+        neighbours = spatial_weights.neighbors[row[unique_id]]
+        neighbours.append(row[unique_id])
+
+        values_list = gdf.loc[gdf[unique_id].isin(neighbours)][values]
+
         if rng:
             from momepy import limit_range
-            values_list = limit_range(values_list, rng=rng)
-        results_list.append(sum(values_list) / len(values_list))
+            values_list = limit_range(values_list.tolist(), rng=rng)
+        results_list.append(np.mean(values_list))
 
     series = pd.Series(results_list)
 
@@ -622,7 +595,7 @@ def street_profile(left, right, heights=None, distance=10, tick_length=50):
     return street_profile
 
 
-def weighted_character(left, right, characters, unique_id, spatial_weights=None, areas=None, order=3):
+def weighted_character(gdf, values, spatial_weights, unique_id, areas=None):
     """
     Calculates the weighted character
 
@@ -633,19 +606,17 @@ def weighted_character(left, right, characters, unique_id, spatial_weights=None,
 
     Parameters
     ----------
-    left : GeoDataFrame
+    gdf : GeoDataFrame
         GeoDataFrame containing objects to analyse
-    right : GeoDataFrame
-        GeoDataFrame containing morphological tessellation
-    characters : str, list, np.array, pd.Series (default None)
-        the name of the left dataframe column, np.array, or pd.Series where is stored character to be weighted
-    spatial_weights : libpysal.weights (default None)
+    values : str, list, np.array, pd.Series
+        the name of the gdf dataframe column, np.array, or pd.Series where is stored character to be weighted
+    spatial_weights : libpysal.weights
         spatial weights matrix - If None, Queen contiguity matrix of set order will be calculated
         based on left.
+    unique_id : str
+        name of the column with unique id used as spatial_weights index.
     areas : str, list, np.array, pd.Series (default None)
         the name of the left dataframe column, np.array, or pd.Series where is stored area value
-    order : int (default 3)
-        order of Queen contiguity. Used only when spatial_weights=None.
 
 
     Returns
@@ -675,50 +646,41 @@ def weighted_character(left, right, characters, unique_id, spatial_weights=None,
     # define empty list for results
     results_list = []
 
-    if spatial_weights is None:
-        print('Generating weights matrix (Queen) of {} topological steps...'.format(order))
-        from momepy import Queen_higher
-        # matrix to define area of analysis (more steps)
-        spatial_weights = Queen_higher(k=order, geodataframe=right)
-    else:
-        if not all(left.index == range(len(left))):
-            raise ValueError('Index is not consecutive range 0:x, spatial weights will not match objects.')
-
-    print('Calculating weighted {}...'.format(characters))
-
+    print('Calculating weighted {}...'.format(values))
+    gdf = gdf.copy()
     if areas is not None:
         if not isinstance(areas, str):
-            left = left.copy()
-            left['mm_a'] = areas
+            gdf['mm_a'] = areas
             areas = 'mm_a'
+    if not isinstance(values, str):
+        gdf['mm_vals'] = values
+        values = 'mm_a'
 
-    for index, row in tqdm(left.iterrows(), total=left.shape[0]):
-        uid = right.loc[right[unique_id] == row[unique_id]].index[0]
-        neighbours = spatial_weights.neighbors[uid]
+    for index, row in tqdm(gdf.iterrows(), total=gdf.shape[0]):
+        neighbours = spatial_weights.neighbors[row[unique_id]]
 
         if neighbours:
-            neighbours_ids = right.iloc[neighbours][unique_id]
-            building_neighbours = left.loc[left[unique_id].isin(neighbours_ids)]
+            building_neighbours = gdf.loc[gdf[unique_id].isin(neighbours)]
 
             if areas is not None:
-                results_list.append((sum(building_neighbours[characters]
+                results_list.append((sum(building_neighbours[values]
                                          * building_neighbours[areas])
-                                    + (row[characters] * row[areas]))
+                                    + (row[values] * row[areas]))
                                     / (sum(building_neighbours[areas]) + row[areas]))
             else:
-                results_list.append((sum(building_neighbours[characters]
+                results_list.append((sum(building_neighbours[values]
                                          * building_neighbours.geometry.area)
-                                    + (row[characters] * row.geometry.area))
+                                    + (row[values] * row.geometry.area))
                                     / (sum(building_neighbours.geometry.area) + row.geometry.area))
         else:
-            results_list.append(row[characters])
+            results_list.append(row[values])
     series = pd.Series(results_list)
 
-    print('Weighted {} calculated.'.format(characters))
+    print('Weighted {} calculated.'.format(values))
     return series
 
 
-def covered_area(gdf, spatial_weights=None):
+def covered_area(gdf, spatial_weights, unique_id):
     """
     Calculates the area covered by k steps of morphological tessellation
 
@@ -732,8 +694,9 @@ def covered_area(gdf, spatial_weights=None):
     gdf : GeoDataFrame
         GeoDataFrame containing morphological tessellation
     spatial_weights : libpysal.weights, optional
-        spatial weights matrix - If None, Queen contiguity matrix will be calculated
-        based on gdf.
+        spatial weights matrix
+    unique_id : str
+        name of the column with unique id used as spatial_weights index.
 
     Returns
     -------
@@ -752,19 +715,10 @@ def covered_area(gdf, spatial_weights=None):
 
     print('Calculating covered area...')
 
-    if spatial_weights is None:
-        print('Generating weights matrix (Queen)...')
-        from libpysal.weights import Queen
-        # matrix to define area of analysis (more steps)
-        spatial_weights = Queen.from_dataframe(gdf, silence_warnings=True)
-    else:
-        if not all(gdf.index == range(len(gdf))):
-            raise ValueError('Index is not consecutive range 0:x, spatial weights will not match objects.')
-
     for index, row in tqdm(gdf.iterrows(), total=gdf.shape[0]):
-        neighbours = spatial_weights.neighbors[index]
-        neighbours.append(index)
-        areas = gdf.iloc[neighbours].geometry.area
+        neighbours = spatial_weights.neighbors[row[unique_id]]
+        neighbours.append(row[unique_id])
+        areas = gdf.loc[gdf[unique_id].isin(neighbours)].geometry.area
         results_list.append(sum(areas))
 
     series = pd.Series(results_list)
@@ -783,7 +737,7 @@ def wall(gdf, spatial_weights=None):
         GeoDataFrame containing objects to analyse
     spatial_weights : libpysal.weights, optional
         spatial weights matrix - If None, Queen contiguity matrix will be calculated
-        based on gdf. It is to denote adjacent buildings.
+        based on gdf. It is to denote adjacent buildings (note: based on index, not ID).
 
     Returns
     -------
@@ -853,10 +807,10 @@ def segments_length(gdf, spatial_weights=None, mean=False):
     Parameters
     ----------
     gdf : GeoDataFrame
-        GeoDataFrame containing streets (segments) to analyse
+        GeoDataFrame containing streets (edges) to analyse
     spatial_weights : libpysal.weights, optional
         spatial weights matrix - If None, Queen contiguity matrix will be calculated
-        based on streets.
+        based on streets (note: based on index, not ID).
     mean : boolean, optional
         If mean=False it will return total length, if mean=True it will return mean value
 
diff --git a/momepy/distribution.py b/momepy/distribution.py
index 13054f4..68a30bb 100644
--- a/momepy/distribution.py
+++ b/momepy/distribution.py
@@ -339,7 +339,7 @@ def cell_alignment(left, right, left_orientations, right_orientations, unique_id
     return series
 
 
-def alignment(left, right, orientations, unique_id, spatial_weights=None):
+def alignment(gdf, spatial_weights, unique_id, orientations):
 
     """
     Calculate the mean deviation of solar orientation of objects on adjacent cells from an object
@@ -349,21 +349,15 @@ def alignment(left, right, orientations, unique_id, spatial_weights=None):
 
     Parameters
     ----------
-    left : GeoDataFrame
+    gdf : GeoDataFrame
         GeoDataFrame containing objects to analyse
-    right : GeoDataFrame
-        GeoDataFrame containing morphological tessellation - source of spatial_weights.
-        It is crucial to use exactly same input as was used during the calculation of weights matrix.
-        If spatial_weights is None, tessellation is used to calulate it.
+    spatial_weights : libpysal.weights, optional
+        spatial weights matrix
     orientations : str, list, np.array, pd.Series
         the name of the left dataframe column, np.array, or pd.Series where is stored object orientation value
         (can be calculated using :py:func:`momepy.orientation`)
     unique_id : str
-        the name of the dataframe column with unique id shared between a cell and a building
-        (must be present in both geodataframes)
-    spatial_weights : libpysal.weights, optional
-        spatial weights matrix - If None, Queen contiguity matrix will be calculated
-        based on tessellation
+        name of the column with unique id used as spatial_weights index.
 
     Returns
     -------
@@ -372,7 +366,7 @@ def alignment(left, right, orientations, unique_id, spatial_weights=None):
 
     Examples
     --------
-    >>> buildings_df['alignment'] = momepy.alignment(buildings_df, 'bl_orient', tessellation_df, 'uID')
+    >>> buildings_df['alignment'] = momepy.alignment(buildings_df, sw, 'uID', bl_orient)
     Calculating alignments...
     Calculating spatial weights...
     Spatial weights ready...
@@ -383,43 +377,21 @@ def alignment(left, right, orientations, unique_id, spatial_weights=None):
     """
     # define empty list for results
     results_list = []
-    left = left.copy()
+    gdf = gdf.copy()
     if not isinstance(orientations, str):
-        left['mm_o'] = orientations
+        gdf['mm_o'] = orientations
         orientations = 'mm_o'
 
     print('Calculating alignments...')
 
-    if not all(right.index == range(len(right))):
-        raise ValueError('Index is not consecutive range 0:x, spatial weights will not match objects.')
-
-    if spatial_weights is None:
-        print('Calculating spatial weights...')
-        from libpysal.weights import Queen
-        spatial_weights = Queen.from_dataframe(right)
-        print('Spatial weights ready...')
-
     # iterating over rows one by one
-    for index, row in tqdm(left.iterrows(), total=left.shape[0]):
-        uid = right.loc[right[unique_id] == row[unique_id]].index[0]
-        neighbours = spatial_weights.neighbors[uid]
-        neighbours_ids = []
-
-        for n in neighbours:
-            uniq = right.iloc[n][unique_id]
-            neighbours_ids.append(uniq)
-
-        orientation = []
-        for i in neighbours_ids:
-            ori = left.loc[left[unique_id] == i].iloc[0][orientations]
-            orientation.append(ori)
+    for index, row in tqdm(gdf.iterrows(), total=gdf.shape[0]):
 
-        deviations = []
-        for o in orientation:
-            dev = abs(o - row[orientations])
-            deviations.append(dev)
+        neighbours = spatial_weights.neighbors[row[unique_id]]
+        if neighbours:
+            orientation = gdf.loc[gdf[unique_id].isin(neighbours)][orientations]
+            deviations = abs(orientation - row[orientations])
 
-        if deviations:
             results_list.append(statistics.mean(deviations))
         else:
             results_list.append(0)
@@ -430,26 +402,21 @@ def alignment(left, right, orientations, unique_id, spatial_weights=None):
     return series
 
 
-def neighbour_distance(left, right, unique_id, spatial_weights=None):
+def neighbour_distance(gdf, spatial_weights, unique_id):
     """
-    Calculate the mean distance to buildings on adjacent cells
+    Calculate the mean distance to adjacent buildings (based on spatial_weights)
 
     .. math::
         \\frac{1}{n}\\sum_{i=1}^n dist_i=\\frac{dist_1+dist_2+\\cdots+dist_n}{n}
 
     Parameters
     ----------
-    left : GeoDataFrame
+    gdf : GeoDataFrame
         GeoDataFrame containing objects to analyse
-    right : GeoDataFrame
-        GeoDataFrame containing morphological tessellation - source of spatial_weights.
-        It is crucial to use exactly same input as was used during the calculation of weights matrix.
-        If spatial_weights is None, tessellation is used to calulate it.
-    unique_id : str
-        name of the column with unique id
     spatial_weights : libpysal.weights, optional
-        spatial weights matrix - If None, Queen contiguity matrix will be calculated
-        based on tessellation
+        spatial weights matrix
+    unique_id : str
+        name of the column with unique id used as spatial_weights index.
 
     Returns
     -------
@@ -463,10 +430,8 @@ def neighbour_distance(left, right, unique_id, spatial_weights=None):
 
     Examples
     --------
-    >>> buildings_df['neighbour_distance'] = momepy.neighbour_distance(buildings_df, tessellation_df, 'uID')
+    >>> buildings_df['neighbour_distance'] = momepy.neighbour_distance(buildings_df, sw, 'uID')
     Calculating distances...
-    Calculating spatial weights...
-    Spatial weights ready...
     100%|██████████| 144/144 [00:00<00:00, 345.78it/s]
     Distances calculated.
     >>> buildings_df['neighbour_distance'][0]
@@ -477,22 +442,10 @@ def neighbour_distance(left, right, unique_id, spatial_weights=None):
 
     print('Calculating distances...')
 
-    if not all(right.index == range(len(right))):
-        raise ValueError('Index is not consecutive range 0:x, spatial weights will not match objects.')
-
-    if spatial_weights is None:
-        print('Calculating spatial weights...')
-        from libpysal.weights import Queen
-        spatial_weights = Queen.from_dataframe(right)
-        print('Spatial weights ready...')
-
     # iterating over rows one by one
-    for index, row in tqdm(left.iterrows(), total=left.shape[0]):
-        uid = right.loc[right[unique_id] == row[unique_id]].index[0]
-        neighbours = spatial_weights.neighbors[uid]
-
-        neighbours_ids = right.iloc[neighbours][unique_id]
-        building_neighbours = left.loc[left[unique_id].isin(neighbours_ids)]
+    for index, row in tqdm(gdf.iterrows(), total=gdf.shape[0]):
+        neighbours = spatial_weights.neighbors[row[unique_id]]
+        building_neighbours = gdf.loc[gdf[unique_id].isin(neighbours)]
         if len(building_neighbours) > 0:
             results_list.append(np.mean(building_neighbours.geometry.distance(row['geometry'])))
         else:
@@ -504,7 +457,7 @@ def neighbour_distance(left, right, unique_id, spatial_weights=None):
     return series
 
 
-def mean_interbuilding_distance(left, right, unique_id, spatial_weights=None, spatial_weights_higher=None, order=3):
+def mean_interbuilding_distance(gdf, spatial_weights, unique_id, spatial_weights_higher=None, order=3):
     """
     Calculate the mean interbuilding distance within x topological steps
 
@@ -516,20 +469,15 @@ def mean_interbuilding_distance(left, right, unique_id, spatial_weights=None, sp
 
     Parameters
     ----------
-    left : GeoDataFrame
+    gdf : GeoDataFrame
         GeoDataFrame containing objects to analyse
-    right : GeoDataFrame
-        GeoDataFrame containing morphological tessellation - source of spatial_weights and spatial_weights_higher.
-        It is crucial to use exactly same input as was used during the calculation of weights matrix and spatial_weights_higher.
-        If spatial_weights or spatial_weights_higher is None, tessellation is used to calulate it.
     unique_id : str
-        name of the column with unique id
-    spatial_weights : libpysal.weights, optional
-        spatial weights matrix - If None, Queen contiguity matrix will be calculated
-        based on tessellation
+        name of the column with unique id used as spatial_weights index
+    spatial_weights : libpysal.weights
+        spatial weights matrix (order 1)
     spatial_weights_higher : libpysal.weights, optional
         spatial weights matrix - If None, Queen contiguity of higher order will be calculated
-        based on tessellation
+        based on spatial_weights
     order : int
         Order of Queen contiguity
 
@@ -548,9 +496,8 @@ def mean_interbuilding_distance(left, right, unique_id, spatial_weights=None, sp
 
     Examples
     --------
-    >>> buildings_df['mean_interbuilding_distance'] = momepy.mean_interbuilding_distance(buildings_df, tessellation_df, 'uID')
+    >>> buildings_df['mean_interbuilding_distance'] = momepy.mean_interbuilding_distance(buildings_df, sw, 'uID')
     Calculating mean interbuilding distances...
-    Generating weights matrix (Queen)...
     Generating weights matrix (Queen) of 3 topological steps...
     Generating adjacency matrix based on weights matrix...
     Computing interbuilding distances...
@@ -561,21 +508,13 @@ def mean_interbuilding_distance(left, right, unique_id, spatial_weights=None, sp
     >>> buildings_df['mean_interbuilding_distance'][0]
     29.305457092042744
     """
-    if not all(right.index == range(len(right))):
-        raise ValueError('Index is not consecutive range 0:x, spatial weights will not match objects.')
 
     print('Calculating mean interbuilding distances...')
-    if spatial_weights is None:
-        print('Generating weights matrix (Queen)...')
-        from libpysal.weights import Queen
-        # matrix to capture interbuilding relationship
-        spatial_weights = Queen.from_dataframe(right)
-
     if spatial_weights_higher is None:
         print('Generating weights matrix (Queen) of {} topological steps...'.format(order))
         from momepy import Queen_higher
         # matrix to define area of analysis (more steps)
-        spatial_weights_higher = Queen_higher(k=order, geodataframe=right)
+        spatial_weights_higher = Queen_higher(k=order, weights=spatial_weights)
 
     # define empty list for results
     results_list = []
@@ -590,20 +529,18 @@ def mean_interbuilding_distance(left, right, unique_id, spatial_weights=None, sp
     for index, row in tqdm(adj_list.iterrows(), total=adj_list.shape[0]):
         inverted = adj_list[(adj_list.focal == row.neighbor)][(adj_list.neighbor == row.focal)].iloc[0]['distance']
         if inverted == -1:
-            object_id = right.iloc[row.focal.astype(int)][unique_id]
-            building_object = left.loc[left[unique_id] == object_id]
+            building_object = gdf.loc[gdf[unique_id] == row.focal.astype(int)]
 
-            neighbours_id = right.iloc[row.neighbor.astype(int)][unique_id]
-            building_neighbour = left.loc[left[unique_id] == neighbours_id]
+            building_neighbour = gdf.loc[gdf[unique_id] == row.neighbor.astype(int)]
             adj_list.loc[index, 'distance'] = building_neighbour.iloc[0].geometry.distance(building_object.iloc[0].geometry)
         else:
             adj_list.at[index, 'distance'] = inverted
 
     print('Computing mean interbuilding distances...')
     # iterate over objects to get the final values
-    for index, row in tqdm(left.iterrows(), total=left.shape[0]):
+    for index, row in tqdm(gdf.iterrows(), total=gdf.shape[0]):
         # id to match spatial weights
-        uid = right.loc[right[unique_id] == row[unique_id]].index[0]
+        uid = row[unique_id]
         # define neighbours based on weights matrix defining analysis area
         neighbours = spatial_weights_higher.neighbors[uid]
         neighbours.append(uid)
@@ -713,7 +650,7 @@ def neighbouring_street_orientation_deviation(gdf):
     return series
 
 
-def building_adjacency(left, right, spatial_weights=None, spatial_weights_higher=None, order=3, unique_id='uID'):
+def building_adjacency(gdf, spatial_weights_higher, unique_id, spatial_weights=None):
     """
     Calculate the level of building adjacency
 
@@ -725,20 +662,15 @@ def building_adjacency(left, right, spatial_weights=None, spatial_weights_higher
 
     Parameters
     ----------
-    left : GeoDataFrame
+    gdf : GeoDataFrame
         GeoDataFrame containing objects to analyse
-    right : GeoDataFrame
-        GeoDataFrame containing morphological tessellation - source of spatial_weights and spatial_weights_higher.
-        It is crucial to use exactly same input as was used durign the calculation of weights matrix and spatial_weights_higher.
-        If spatial_weights or spatial_weights_higher is None, tessellation is used to calulate it.
+    spatial_weights_higher : libpysal.weights, optional
+        spatial weights matrix
+    unique_id : str
+        name of the column with unique id used as spatial_weights index
     spatial_weights : libpysal.weights, optional
         spatial weights matrix - If None, Queen contiguity matrix will be calculated
-        based on tessellation
-    spatial_weights_higher : libpysal.weights, optional
-        spatial weights matrix - If None, Queen contiguity of higher order will be calculated
-        based on tessellation
-    order : int
-        Order of Queen contiguity
+        based on gdf. It is to denote adjacent buildings (note: based on index, not ID).
 
     Returns
     -------
@@ -752,7 +684,7 @@ def building_adjacency(left, right, spatial_weights=None, spatial_weights_higher
 
     Examples
     --------
-    >>> buildings_df['adjacency'] = momepy.building_adjacency(buildings_df, tessellation_df, unique_id='uID')
+    >>> buildings_df['adjacency'] = momepy.building_adjacency(buildings_df, swh, unique_id='uID')
     Calculating adjacency...
     Calculating spatial weights...
     Spatial weights ready...
@@ -770,37 +702,29 @@ def building_adjacency(left, right, spatial_weights=None, spatial_weights_higher
 
     print('Calculating adjacency...')
 
-    if not all(right.index == range(len(right))):
-        raise ValueError('Index is not consecutive range 0:x, spatial weights will not match objects.')
-
-    # if weights matrix is not passed, generate it from objects
+    # if weights matrix is not passed, generate it from gdf
     if spatial_weights is None:
         print('Calculating spatial weights...')
         from libpysal.weights import Queen
-        spatial_weights = Queen.from_dataframe(left, silence_warnings=True)
+        spatial_weights = Queen.from_dataframe(gdf, silence_warnings=True, ids=unique_id)
         print('Spatial weights ready...')
 
-    if spatial_weights_higher is None:
-        print('Generating weights matrix (Queen) of {} topological steps...'.format(order))
-        from momepy import Queen_higher
-        # matrix to define area of analysis (more steps)
-        spatial_weights_higher = Queen_higher(k=order, geodataframe=right)
-
     print('Generating dictionary of built-up patches...')
     # dict to store nr of courtyards for each uID
     patches = {}
     jID = 1
-    for index, row in tqdm(left.iterrows(), total=left.shape[0]):
+    for index, row in tqdm(gdf.iterrows(), total=gdf.shape[0]):
 
         # if the id is already present in courtyards, continue (avoid repetition)
-        if index in patches:
+        if row[unique_id] in patches:
             continue
         else:
-            to_join = [index]  # list of indices which should be joined together
+            to_join = [row[unique_id]]  # list of indices which should be joined together
             neighbours = []  # list of neighbours
-            weights = spatial_weights.neighbors[index]  # neighbours from spatial weights
+            weights = spatial_weights.neighbors[row[unique_id]]  # neighbours from spatial weights
             for w in weights:
                 neighbours.append(w)  # make a list from weigths
+            neighbours = spatial_weights.neighbors[row[unique_id]]
 
             for n in neighbours:
                 while n not in to_join:  # until there is some neighbour which is not in to_join
@@ -813,18 +737,17 @@ def building_adjacency(left, right, spatial_weights=None, spatial_weights_higher
             jID = jID + 1
 
     print('Calculating adjacency within k steps...')
-    for index, row in tqdm(left.iterrows(), total=left.shape[0]):
-        uid = right.loc[right[unique_id] == row[unique_id]].index[0]
-        neighbours = spatial_weights_higher.neighbors[uid]
+    for index, row in tqdm(gdf.iterrows(), total=gdf.shape[0]):
+        neighbours = spatial_weights_higher.neighbors[row[unique_id]]
+        if neighbours:
+            neighbours.append(row[unique_id])
 
-        neighbours_ids = right.iloc[neighbours][unique_id]
-        neighbours_ids = neighbours_ids.append(pd.Series(row[unique_id], index=[index]))
-        building_neighbours = left.loc[left[unique_id].isin(neighbours_ids)]
-        indices = list(building_neighbours.index)
-        patches_sub = [patches[x] for x in indices]
-        patches_nr = len(set(patches_sub))
+            patches_sub = [patches[x] for x in neighbours]
+            patches_nr = len(set(patches_sub))
 
-        results_list.append(patches_nr / len(building_neighbours))
+            results_list.append(patches_nr / len(neighbours))
+        else:
+            results_list.append(0)
 
     series = pd.Series(results_list)
 
@@ -832,7 +755,7 @@ def building_adjacency(left, right, spatial_weights=None, spatial_weights_higher
     return series
 
 
-def neighbours(gdf, spatial_weights=None, weighted=False):
+def neighbours(gdf, spatial_weights, unique_id, weighted=False):
     """
     Calculate the number of topological neighbours of each object.
 
@@ -847,8 +770,9 @@ def neighbours(gdf, spatial_weights=None, weighted=False):
     gdf : GeoDataFrame
         GeoDataFrame containing objects to analyse
     spatial_weights : libpysal.weights (default None)
-        spatial weights matrix - If None, Queen contiguity matrix will be calculated
-        based on tessellation
+        spatial weights matrix
+    unique_id : str
+        name of the column with unique id used as spatial_weights index
     weighted : bool (default False)
         if weighted=True, number of neighbours will be divided by the perimeter of object, to return relative value
 
@@ -875,23 +799,13 @@ def neighbours(gdf, spatial_weights=None, weighted=False):
     4
     """
 
-    if not all(gdf.index == range(len(gdf))):
-        raise ValueError('Index is not consecutive range 0:x, spatial weights will not match objects.')
-
-    # if weights matrix is not passed, generate it from objects
-    if spatial_weights is None:
-        print('Calculating spatial weights...')
-        from libpysal.weights import Queen
-        spatial_weights = Queen.from_dataframe(gdf, silence_warnings=True)
-        print('Spatial weights ready...')
-
     print('Calculating neighbours...')
     neighbours = []
     for index, row in tqdm(gdf.iterrows(), total=gdf.shape[0]):
         if weighted is True:
-            neighbours.append(spatial_weights.cardinalities[index] / row.geometry.length)
+            neighbours.append(spatial_weights.cardinalities[row[unique_id]] / row.geometry.length)
         else:
-            neighbours.append(spatial_weights.cardinalities[index])
+            neighbours.append(spatial_weights.cardinalities[row[unique_id]])
 
     series = pd.Series(neighbours)
     print('Neighbours calculated.')
diff --git a/momepy/diversity.py b/momepy/diversity.py
index 0599e56..06a5e9e 100644
--- a/momepy/diversity.py
+++ b/momepy/diversity.py
@@ -9,7 +9,7 @@ import pandas as pd
 import scipy as sp
 
 
-def rng(gdf, values, spatial_weights=None, order=None, rng=(0, 100), **kwargs):
+def rng(gdf, values, spatial_weights, unique_id, rng=(0, 100), **kwargs):
     """
     Calculates the range of values within k steps of morphological tessellation
 
@@ -24,11 +24,10 @@ def rng(gdf, values, spatial_weights=None, order=None, rng=(0, 100), **kwargs):
         GeoDataFrame containing morphological tessellation
     values : str, list, np.array, pd.Series
         the name of the dataframe column, np.array, or pd.Series where is stored character value.
-    spatial_weights : libpysal.weights, optional
-        spatial weights matrix - If None, Queen contiguity matrix of set order will be calculated
-        based on objects.
-    order : int
-        order of Queen contiguity
+    spatial_weights : libpysal.weights
+        spatial weights matrix
+    unique_id : str
+        name of the column with unique id used as spatial_weights index
     rng : Two-element sequence containing floats in range of [0,100], optional
         Percentiles over which to compute the range. Each must be
         between 0 and 100, inclusive. The order of the elements is not important.
@@ -52,27 +51,18 @@ def rng(gdf, values, spatial_weights=None, order=None, rng=(0, 100), **kwargs):
     gdf = gdf.copy()
     print('Calculating range...')
 
-    if spatial_weights is None:
-        print('Generating weights matrix (Queen) of {} topological steps...'.format(order))
-        from momepy import Queen_higher
-        # matrix to define area of analysis (more steps)
-        spatial_weights = Queen_higher(k=order, geodataframe=gdf)
-    else:
-        if not all(gdf.index == range(len(gdf))):
-            raise ValueError('Index is not consecutive range 0:x, spatial weights will not match objects.')
-
     if values is not None:
         if not isinstance(values, str):
             gdf['mm_v'] = values
             values = 'mm_v'
 
     for index, row in tqdm(gdf.iterrows(), total=gdf.shape[0]):
-        neighbours = spatial_weights.neighbors[index]
-        values_list = gdf.iloc[neighbours][values].tolist()
-        if values_list:
-            values_list.append(row[values])
+        neighbours = spatial_weights.neighbors[row[unique_id]]
+        if neighbours:
+            neighbours.append(row[unique_id])
         else:
-            values_list = [row[values]]
+            neighbours = row[unique_id]
+        values_list = gdf.loc[gdf[unique_id].isin(neighbours)][values]
 
         results_list.append(sp.stats.iqr(values_list, rng=rng, **kwargs))
 
@@ -82,7 +72,7 @@ def rng(gdf, values, spatial_weights=None, order=None, rng=(0, 100), **kwargs):
     return series
 
 
-def theil(gdf, values, spatial_weights=None, order=None, rng=(0, 100)):
+def theil(gdf, values, spatial_weights, unique_id, rng=(0, 100)):
     """
     Calculates the Theil measure of inequality of values within k steps of morphological tessellation
 
@@ -97,11 +87,10 @@ def theil(gdf, values, spatial_weights=None, order=None, rng=(0, 100)):
         GeoDataFrame containing morphological tessellation
     values : str, list, np.array, pd.Series
         the name of the dataframe column, np.array, or pd.Series where is stored character value.
-    spatial_weights : libpysal.weights, optional
-        spatial weights matrix - If None, Queen contiguity matrix of set order will be calculated
-        based on objects.
-    order : int
-        order of Queen contiguity
+    spatial_weights : libpysal.weights
+        spatial weights matrix
+    unique_id : str
+        name of the column with unique id used as spatial_weights index
     rng : Two-element sequence containing floats in range of [0,100], optional
         Percentiles over which to compute the range. Each must be
         between 0 and 100, inclusive. The order of the elements is not important.
@@ -125,31 +114,22 @@ def theil(gdf, values, spatial_weights=None, order=None, rng=(0, 100)):
 
     print('Calculating Theil index...')
 
-    if spatial_weights is None:
-        print('Generating weights matrix (Queen) of {} topological steps...'.format(order))
-        from momepy import Queen_higher
-        # matrix to define area of analysis (more steps)
-        spatial_weights = Queen_higher(k=order, geodataframe=gdf)
-    else:
-        if not all(gdf.index == range(len(gdf))):
-            raise ValueError('Index is not consecutive range 0:x, spatial weights will not match objects.')
-
     if values is not None:
         if not isinstance(values, str):
             gdf['mm_v'] = values
             values = 'mm_v'
 
     for index, row in tqdm(gdf.iterrows(), total=gdf.shape[0]):
-        neighbours = spatial_weights.neighbors[index]
-        values_list = gdf.iloc[neighbours][values].tolist()
-        if values_list:
-            values_list.append(row[values])
+        neighbours = spatial_weights.neighbors[row[unique_id]]
+        if neighbours:
+            neighbours.append(row[unique_id])
         else:
-            values_list = [row[values]]
+            neighbours = row[unique_id]
+        values_list = gdf.loc[gdf[unique_id].isin(neighbours)][values]
 
         if rng:
             from momepy import limit_range
-            values_list = limit_range(values_list, rng=rng)
+            values_list = limit_range(values_list.tolist(), rng=rng)
         results_list.append(Theil(values_list).T)
 
     series = pd.Series(results_list)
@@ -158,7 +138,7 @@ def theil(gdf, values, spatial_weights=None, order=None, rng=(0, 100)):
     return series
 
 
-def simpson(gdf, values, spatial_weights=None, order=None, binning='HeadTailBreaks', **classification_kwds):
+def simpson(gdf, values, spatial_weights, unique_id, binning='HeadTailBreaks', **classification_kwds):
     """
     Calculates the Simpson\'s diversity index of values within k steps of morphological tessellation
 
@@ -243,15 +223,6 @@ def simpson(gdf, values, spatial_weights=None, order=None, binning='HeadTailBrea
     gdf = gdf.copy()
     print('Calculating Simpson\'s diversity index...')
 
-    if spatial_weights is None:
-        print('Generating weights matrix (Queen) of {} topological steps...'.format(order))
-        from momepy import Queen_higher
-        # matrix to define area of analysis (more steps)
-        spatial_weights = Queen_higher(k=order, geodataframe=gdf)
-    else:
-        if not all(gdf.index == range(len(gdf))):
-            raise ValueError('Index is not consecutive range 0:x, spatial weights will not match objects.')
-
     if values is not None:
         if not isinstance(values, str):
             gdf['mm_v'] = values
@@ -260,12 +231,12 @@ def simpson(gdf, values, spatial_weights=None, order=None, binning='HeadTailBrea
     bins = schemes[binning](gdf[values], **classification_kwds).bins
 
     for index, row in tqdm(gdf.iterrows(), total=gdf.shape[0]):
-        neighbours = spatial_weights.neighbors[index]
-        values_list = gdf.iloc[neighbours][values].tolist()
-        if values_list:
-            values_list.append(row[values])
+        neighbours = spatial_weights.neighbors[row[unique_id]]
+        if neighbours:
+            neighbours.append(row[unique_id])
         else:
-            values_list = [row[values]]
+            neighbours = row[unique_id]
+        values_list = gdf.loc[gdf[unique_id].isin(neighbours)][values]
 
         sample_bins = mapclassify.classifiers.User_Defined(values_list, bins)
         counts = dict(zip(bins, sample_bins.counts))
diff --git a/momepy/intensity.py b/momepy/intensity.py
index 2435458..12a719c 100644
--- a/momepy/intensity.py
+++ b/momepy/intensity.py
@@ -10,7 +10,7 @@ import numpy as np
 import collections
 
 
-def covered_area_ratio(left, right, left_areas, right_areas, unique_id="uID"):
+def covered_area_ratio(left, right, left_areas, right_areas, unique_id):
     """
     Calculate covered area ratio of objects.
 
@@ -62,7 +62,7 @@ def covered_area_ratio(left, right, left_areas, right_areas, unique_id="uID"):
     return series
 
 
-def floor_area_ratio(left, right, left_areas, right_areas, unique_id="uID"):
+def floor_area_ratio(left, right, left_areas, right_areas, unique_id):
     """
     Calculate floor area ratio of objects.
 
@@ -180,7 +180,7 @@ def courtyards(gdf, block_id, spatial_weights=None):
         name of the column where is stored block ID
     spatial_weights : libpysal.weights, optional
         spatial weights matrix - If None, Queen contiguity matrix will be calculated
-        based on objects. It is to denote adjacent buildings.
+        based on objects. It is to denote adjacent buildings (note: based on index).
 
     Returns
     -------
@@ -196,9 +196,6 @@ def courtyards(gdf, block_id, spatial_weights=None):
 
     print('Calculating courtyards...')
 
-    if not all(gdf.index == range(len(gdf))):
-        raise ValueError('Index is not consecutive range 0:x, spatial weights will not match objects.')
-
     # if weights matrix is not passed, generate it from objects
     if spatial_weights is None:
         print('Calculating spatial weights...')
@@ -243,27 +240,25 @@ def courtyards(gdf, block_id, spatial_weights=None):
     return series
 
 
-def blocks_count(gdf, block_id, spatial_weights=None, order=5):
+def blocks_count(gdf, block_id, spatial_weights, unique_id):
     """
     Calculates the weighted number of blocks
 
     Number of blocks within `k` topological steps defined in spatial_weights weighted by the analysed area.
 
     .. math::
-        \\frac{\\sum_{i=1}^{n} {blocks}}{\\sum_{i=1}^{n} area_{i}}
-        NOT SURE
+
 
     Parameters
     ----------
     gdf : GeoDataFrame
         GeoDataFrame containing morphological tessellation
-    block_id : str, list, np.array, pd.Series (default None)
+    block_id : str, list, np.array, pd.Series
         the name of the objects dataframe column, np.array, or pd.Series where is stored block ID.
-    spatial_weights : libpysal.weights (default None)
-        spatial weights matrix - If None, Queen contiguity matrix of set order will be calculated
-        based on objects.
-    order : int (default 5)
-        order of Queen contiguity. Used only when spatial_weights=None.
+    spatial_weights : libpysal.weights
+        spatial weights matrix
+    unique_id : str
+        name of the column with unique id used as spatial_weights index.
 
 
     Returns
@@ -290,21 +285,15 @@ def blocks_count(gdf, block_id, spatial_weights=None, order=5):
         block_id['mm_bid'] = block_id
         block_id = 'mm_bid'
 
-    if not all(gdf.index == range(len(gdf))):
-        raise ValueError('Index is not consecutive range 0:x, spatial weights will not match objects.')
-
-    if spatial_weights is None:
-        print('Generating weights matrix (Queen) of {} topological steps...'.format(order))
-        from momepy import Queen_higher
-        # matrix to define area of analysis (more steps)
-        spatial_weights = Queen_higher(k=order, geodataframe=gdf)
-
     print('Calculating blocks...')
 
     for index, row in tqdm(gdf.iterrows(), total=gdf.shape[0]):
-        neighbours = spatial_weights.neighbors[index]
-        neighbours.append(index)
-        vicinity = gdf.iloc[neighbours]
+        neighbours = spatial_weights.neighbors[row[unique_id]]
+        if neighbours:
+            neighbours.append(row[unique_id])
+        else:
+            neighbours = row[unique_id]
+        vicinity = gdf.loc[gdf[unique_id].isin(neighbours)]
 
         results_list.append(len(set(list(vicinity[block_id]))) / sum(vicinity.geometry.area))
 
@@ -316,7 +305,7 @@ def blocks_count(gdf, block_id, spatial_weights=None, order=5):
 
 def reached(left, right, unique_id, spatial_weights=None, mode='count', values=None):
     """
-    Calculates the number of objects reached within topological steps
+    Calculates the number of objects reached within topological steps on street network
 
     Number of elements within topological steps defined in spatial_weights. If
     spatial_weights are None, it will assume topological distance 0 (element itself).
@@ -333,7 +322,7 @@ def reached(left, right, unique_id, spatial_weights=None, mode='count', values=N
     right : GeoDataFrame
         GeoDataFrame containing elements to be counted
     unique_id : str, list, np.array, pd.Series (default None)
-        the name of the objects dataframe column, np.array, or pd.Series where is
+        the name of the right dataframe column, np.array, or pd.Series where is
         stored ID of streets (segments or nodes).
     spatial_weights : libpysal.weights (default None)
         spatial weights matrix
@@ -408,7 +397,7 @@ def reached(left, right, unique_id, spatial_weights=None, mode='count', values=N
 
 def node_density(left, right, spatial_weights, weighted=False, node_degree=None, node_start='node_start', node_end='node_end'):
     """
-    Calculate the density of nodes within topological steps defined in spatial_weights.
+    Calculate the density of nodes within topological steps on street network defined in spatial_weights.
 
     Calculated as number of nodes within k steps / cummulative length of street network within k steps.
     node_start and node_end is standard output of :py:func:`momepy.nx_to_gdf` and is compulsory.
diff --git a/momepy/utils.py b/momepy/utils.py
index 4f0e296..e41ea72 100644
--- a/momepy/utils.py
+++ b/momepy/utils.py
@@ -32,7 +32,7 @@ def unique_id(objects):
     return series
 
 
-def Queen_higher(k, geodataframe=None, weights=None):
+def Queen_higher(k, geodataframe=None, weights=None, ids=None):
     """
     Generate spatial weights based on Queen contiguity of order k
 
@@ -68,7 +68,7 @@ def Queen_higher(k, geodataframe=None, weights=None):
     elif geodataframe is not None:
         if not all(geodataframe.index == range(len(geodataframe))):
             raise ValueError('Index is not consecutive range 0:x, spatial weights will not match objects.')
-        first_order = libpysal.weights.Queen.from_dataframe(geodataframe)
+        first_order = libpysal.weights.Queen.from_dataframe(geodataframe, ids=ids)
     else:
         raise Warning('GeoDataFrame of spatial weights must be given.')
 


Test patch:
diff --git a/tests/test_dimension.py b/tests/test_dimension.py
index c119024..6df14df 100644
--- a/tests/test_dimension.py
+++ b/tests/test_dimension.py
@@ -99,27 +99,19 @@ class TestDimensions:
         assert self.df_buildings['long_axis'][0] == check
 
     def test_mean_character(self):
-        self.df_tessellation['mesh'] = mm.mean_character(self.df_tessellation)
-        spatial_weights = Queen_higher(k=3, geodataframe=self.df_tessellation)
-        self.df_tessellation['mesh_sw'] = mm.mean_character(self.df_tessellation, spatial_weights)
+        spatial_weights = Queen_higher(k=3, geodataframe=self.df_tessellation, ids='uID')
         self.df_tessellation['area'] = area = self.df_tessellation.geometry.area
-        self.df_tessellation['mesh_ar'] = mm.mean_character(self.df_tessellation, spatial_weights, values='area')
-        self.df_tessellation['mesh_array'] = mm.mean_character(self.df_tessellation, spatial_weights, values=area)
-        self.df_tessellation['mesh_id'] = mm.mean_character(self.df_tessellation, spatial_weights, values='area', rng=(10, 90))
-        self.df_tessellation['mesh_iq'] = mm.mean_character(self.df_tessellation, spatial_weights, values='area', rng=(25, 75))
-        neighbours = spatial_weights.neighbors[38]
-        total_area = sum(self.df_tessellation.iloc[neighbours].geometry.area) + self.df_tessellation.geometry.area[38]
-        check = total_area / (len(neighbours) + 1)
-        assert self.df_tessellation['mesh'][38] == check
-        assert self.df_tessellation['mesh_sw'][38] == check
-        assert self.df_tessellation['mesh_ar'][38] == check
-        assert self.df_tessellation['mesh_array'][38] == check
+        self.df_tessellation['mesh_ar'] = mm.mean_character(self.df_tessellation, values='area', spatial_weights=spatial_weights, unique_id='uID')
+        self.df_tessellation['mesh_array'] = mm.mean_character(self.df_tessellation, values=area, spatial_weights=spatial_weights, unique_id='uID')
+        self.df_tessellation['mesh_id'] = mm.mean_character(self.df_tessellation, spatial_weights=spatial_weights,
+                                                            values='area', rng=(10, 90), unique_id='uID')
+        self.df_tessellation['mesh_iq'] = mm.mean_character(self.df_tessellation, spatial_weights=spatial_weights,
+                                                            values='area', rng=(25, 75), unique_id='uID')
+        check = 2922.9572601966815
+        assert self.df_tessellation['mesh_ar'][0] == check
+        assert self.df_tessellation['mesh_array'][0] == check
         assert self.df_tessellation['mesh_id'][38] == 2250.2241176070806
-        assert self.df_tessellation['mesh_iq'][38] == 2118.609142733066
-        gdf = self.df_tessellation
-        gdf.index = gdf.index + 20
-        with pytest.raises(ValueError):
-            self.df_tessellation['meshE'] = mm.mean_character(gdf)
+        assert self.df_tessellation['mesh_iq'][38] == 2118.6091427330666
 
     def test_street_profile(self):
         results = mm.street_profile(self.df_streets, self.df_buildings, heights='height')
@@ -140,33 +132,27 @@ class TestDimensions:
         assert results['openness'][0] == 0.5535714285714286
         assert results['heights_deviations'][0] == 5.526848034418866
 
-    def test_weighted_character(self):
-        weighted = mm.weighted_character(self.df_buildings, self.df_tessellation, 'height', 'uID')
-        assert weighted[38] == 18.301521351817303
-
     def test_weighted_character_sw(self):
-        sw = Queen_higher(k=3, geodataframe=self.df_tessellation)
-        weighted = mm.weighted_character(self.df_buildings, self.df_tessellation, 'height', 'uID', sw)
+        sw = Queen_higher(k=3, geodataframe=self.df_tessellation, ids='uID')
+        weighted = mm.weighted_character(self.df_buildings, 'height', sw, 'uID')
         assert weighted[38] == 18.301521351817303
 
     def test_weighted_character_area(self):
         self.df_buildings['area'] = self.df_buildings.geometry.area
-        sw = Queen_higher(k=3, geodataframe=self.df_tessellation)
-        weighted = mm.weighted_character(self.df_buildings, self.df_tessellation, 'height', 'uID', sw, 'area')
+        sw = Queen_higher(k=3, geodataframe=self.df_tessellation, ids='uID')
+        weighted = mm.weighted_character(self.df_buildings, 'height', sw, 'uID', 'area')
         assert weighted[38] == 18.301521351817303
 
     def test_weighted_character_array(self):
         area = self.df_buildings.geometry.area
-        sw = Queen_higher(k=3, geodataframe=self.df_tessellation)
-        weighted = mm.weighted_character(self.df_buildings, self.df_tessellation, 'height', 'uID', sw, area)
+        sw = Queen_higher(k=3, geodataframe=self.df_tessellation, ids='uID')
+        weighted = mm.weighted_character(self.df_buildings, 'height', sw, 'uID', area)
         assert weighted[38] == 18.301521351817303
 
     def test_covered_area(self):
-        sw = Queen_higher(geodataframe=self.df_tessellation, k=1)
-        covered = mm.covered_area(self.df_tessellation)
-        covered_sw = mm.covered_area(self.df_tessellation, sw)
-        assert covered[0] == covered_sw[0]
-        assert covered[0] == 24115.66721833942
+        sw = Queen_higher(geodataframe=self.df_tessellation, k=1, ids='uID')
+        covered_sw = mm.covered_area(self.df_tessellation, sw, 'uID')
+        assert covered_sw[0] == 24115.667218339422
 
     def test_wall(self):
         sw = Queen_higher(geodataframe=self.df_buildings, k=1)
diff --git a/tests/test_distribution.py b/tests/test_distribution.py
index 38f03a7..04ecdfc 100644
--- a/tests/test_distribution.py
+++ b/tests/test_distribution.py
@@ -44,34 +44,27 @@ class TestDistribution:
         self.df_buildings['orient'] = mm.orientation(self.df_buildings)
         self.df_tessellation['orient'] = mm.orientation(self.df_tessellation)
         self.df_buildings['c_align'] = mm.cell_alignment(self.df_buildings, self.df_tessellation, 'orient', 'orient', 'uID')
-        check = abs(self.df_buildings['orient'][0] -
-                    self.df_tessellation[self.df_tessellation['uID'] == self.df_buildings['uID'][0]]['orient'].iloc[0])
+        check = abs(self.df_buildings['orient'][0] - self.df_tessellation[
+            self.df_tessellation['uID'] == self.df_buildings['uID'][0]]['orient'].iloc[0])
         assert self.df_buildings['c_align'][0] == check
 
     def test_alignment(self):
         self.df_buildings['orient'] = mm.orientation(self.df_buildings)
-        self.df_buildings['align'] = mm.alignment(self.df_buildings, self.df_tessellation, 'orient', 'uID')
-        sw = Queen.from_dataframe(self.df_tessellation)
-        self.df_buildings['align_sw'] = mm.alignment(self.df_buildings, self.df_tessellation, 'orient', 'uID', sw)
-        check = 18.299481296455237
-        assert self.df_buildings['align'][0] == check
-        assert self.df_buildings['align_sw'][0] == check
+        sw = Queen.from_dataframe(self.df_tessellation, ids='uID')
+        self.df_buildings['align_sw'] = mm.alignment(self.df_buildings, sw, 'uID', 'orient')
+        assert self.df_buildings['align_sw'][0] == 18.299481296455237
 
     def test_neighbour_distance(self):
-        self.df_buildings['dist'] = mm.neighbour_distance(self.df_buildings, self.df_tessellation, 'uID')
-        sw = Queen.from_dataframe(self.df_tessellation)
-        self.df_buildings['dist_sw'] = mm.neighbour_distance(self.df_buildings, self.df_tessellation, 'uID', sw)
+        sw = Queen.from_dataframe(self.df_tessellation, ids='uID')
+        self.df_buildings['dist_sw'] = mm.neighbour_distance(self.df_buildings, sw, 'uID')
         check = 29.18589019096464
-        assert self.df_buildings['dist'][0] == check
         assert self.df_buildings['dist_sw'][0] == check
 
     def test_mean_interbuilding_distance(self):
-        self.df_buildings['m_dist'] = mm.mean_interbuilding_distance(self.df_buildings, self.df_tessellation, 'uID')
-        sw = Queen.from_dataframe(self.df_tessellation)
-        swh = mm.Queen_higher(k=3, geodataframe=self.df_tessellation)
-        self.df_buildings['m_dist_sw'] = mm.mean_interbuilding_distance(self.df_buildings, self.df_tessellation, 'uID', sw, swh)
+        sw = Queen.from_dataframe(self.df_tessellation, ids='uID')
+        swh = mm.Queen_higher(k=3, geodataframe=self.df_tessellation, ids='uID')
+        self.df_buildings['m_dist_sw'] = mm.mean_interbuilding_distance(self.df_buildings, sw, 'uID', swh)
         check = 29.305457092042744
-        assert self.df_buildings['m_dist'][0] == check
         assert self.df_buildings['m_dist_sw'][0] == check
 
     def test_neighbouring_street_orientation_deviation(self):
@@ -80,21 +73,17 @@ class TestDistribution:
         assert self.df_streets['dev'].mean() == check
 
     def test_building_adjacency(self):
-        self.df_buildings['adj'] = mm.building_adjacency(self.df_buildings, self.df_tessellation)
-        sw = Queen.from_dataframe(self.df_buildings)
-        swh = mm.Queen_higher(k=3, geodataframe=self.df_tessellation)
-        self.df_buildings['adj_sw'] = mm.building_adjacency(self.df_buildings, self.df_tessellation, spatial_weights=sw, spatial_weights_higher=swh)
+        sw = Queen.from_dataframe(self.df_buildings, ids='uID')
+        swh = mm.Queen_higher(k=3, geodataframe=self.df_tessellation, ids='uID')
+        self.df_buildings['adj_sw'] = mm.building_adjacency(self.df_buildings, spatial_weights=sw, unique_id='uID', spatial_weights_higher=swh)
         check = 0.2613824113909074
-        assert self.df_buildings['adj'].mean() == check
         assert self.df_buildings['adj_sw'].mean() == check
 
     def test_neighbours(self):
-        self.df_tessellation['nei'] = mm.neighbours(self.df_tessellation)
-        sw = Queen.from_dataframe(self.df_tessellation)
-        self.df_tessellation['nei_sw'] = mm.neighbours(self.df_tessellation, sw)
-        self.df_tessellation['nei_wei'] = mm.neighbours(self.df_tessellation, sw, weighted=True)
+        sw = Queen.from_dataframe(self.df_tessellation, ids='uID')
+        self.df_tessellation['nei_sw'] = mm.neighbours(self.df_tessellation, sw, 'uID')
+        self.df_tessellation['nei_wei'] = mm.neighbours(self.df_tessellation, sw, 'uID', weighted=True)
         check = 5.180555555555555
         check_w = 0.029066398893536072
-        assert self.df_tessellation['nei'].mean() == check
         assert self.df_tessellation['nei_sw'].mean() == check
         assert self.df_tessellation['nei_wei'].mean() == check_w
diff --git a/tests/test_diversity.py b/tests/test_diversity.py
index c2b0e97..df5d23c 100644
--- a/tests/test_diversity.py
+++ b/tests/test_diversity.py
@@ -1,4 +1,3 @@
-import pytest
 import momepy as mm
 import geopandas as gpd
 import numpy as np
@@ -16,33 +15,25 @@ class TestDiversity:
         self.df_tessellation = gpd.read_file(test_file_path, layer='tessellation')
         self.df_buildings['height'] = np.linspace(10., 30., 144)
         self.df_tessellation['area'] = mm.area(self.df_tessellation)
+        self.sw = Queen_higher(k=3, geodataframe=self.df_tessellation, ids='uID')
 
     def test_rng(self):
-        full = mm.rng(self.df_tessellation, 'area', order=3)
-        assert full[0] == 8255.372874447059
-        sw = Queen_higher(k=3, geodataframe=self.df_tessellation)
-        full_sw = mm.rng(self.df_tessellation, 'area', spatial_weights=sw)
+        full_sw = mm.rng(self.df_tessellation, 'area', self.sw, 'uID')
         assert full_sw[0] == 8255.372874447059
         area = self.df_tessellation['area']
-        full2 = mm.rng(self.df_tessellation, area, order=3)
+        full2 = mm.rng(self.df_tessellation, area, self.sw, 'uID')
         assert full2[0] == 8255.372874447059
-        limit = mm.rng(self.df_tessellation, 'area', spatial_weights=sw, rng=(10, 90))
+        limit = mm.rng(self.df_tessellation, 'area', self.sw, 'uID', rng=(10, 90))
         assert limit[0] == 4122.139212736442
 
     def test_theil(self):
-        full = mm.theil(self.df_tessellation, 'area', order=3)
-        assert full[0] == 0.2574468431886534
-        sw = Queen_higher(k=3, geodataframe=self.df_tessellation)
-        full_sw = mm.theil(self.df_tessellation, 'area', spatial_weights=sw)
-        assert full_sw[0] == 0.2574468431886534
-        limit = mm.theil(self.df_tessellation, 'area', spatial_weights=sw, rng=(10, 90))
+        full_sw = mm.theil(self.df_tessellation, 'area', self.sw, 'uID')
+        assert full_sw[0] == 0.25744684318865324
+        limit = mm.theil(self.df_tessellation, 'area', self.sw, 'uID', rng=(10, 90))
         assert limit[0] == 0.13302952097969373
 
     def test_simpson(self):
-        ht = mm.simpson(self.df_tessellation, 'area', order=3)
-        assert ht[0] == 0.385
-        sw = Queen_higher(k=3, geodataframe=self.df_tessellation)
-        ht_sw = mm.simpson(self.df_tessellation, 'area', spatial_weights=sw)
+        ht_sw = mm.simpson(self.df_tessellation, 'area', self.sw, 'uID')
         assert ht_sw[0] == 0.385
-        quan_sw = mm.simpson(self.df_tessellation, 'area', spatial_weights=sw, binning='quantiles', k=3)
+        quan_sw = mm.simpson(self.df_tessellation, 'area', self.sw, 'uID', binning='quantiles', k=3)
         assert quan_sw[0] == 0.395
diff --git a/tests/test_intensity.py b/tests/test_intensity.py
index 32bb5f9..87c0a94 100644
--- a/tests/test_intensity.py
+++ b/tests/test_intensity.py
@@ -21,12 +21,12 @@ class TestIntensity:
         self.df_buildings, self.df_tessellation, self.blocks = mm.blocks(self.df_tessellation, self.df_streets, self.df_buildings, 'bID', 'uID')
 
     def test_covered_area_ratio(self):
-        car = mm.covered_area_ratio(self.df_tessellation, self.df_buildings, 'area', 'area')
+        car = mm.covered_area_ratio(self.df_tessellation, self.df_buildings, 'area', 'area', 'uID')
         check = 0.3206556897709747
         assert car.mean() == check
 
     def test_floor_area_ratio(self):
-        far = mm.floor_area_ratio(self.df_tessellation, self.df_buildings, 'area', 'fl_area')
+        far = mm.floor_area_ratio(self.df_tessellation, self.df_buildings, 'area', 'fl_area', 'uID')
         check = 1.910949846262234
         assert far.mean() == check
 
@@ -49,11 +49,9 @@ class TestIntensity:
         assert courtyards_wm.mean() == check
 
     def test_blocks_count(self):
-        count = mm.blocks_count(self.df_tessellation, 'bID', spatial_weights=None, order=5)
-        sw = mm.Queen_higher(k=5, geodataframe=self.df_tessellation)
-        count2 = mm.blocks_count(self.df_tessellation, 'bID', spatial_weights=sw)
+        sw = mm.Queen_higher(k=5, geodataframe=self.df_tessellation, ids='uID')
+        count2 = mm.blocks_count(self.df_tessellation, 'bID', sw, 'uID')
         check = 3.142437439120778e-05
-        assert count.mean() == check
         assert count2.mean() == check
 
     def test_reached(self):