Search code examples
pythonfor-looppysal

For loops to a adjacency matrix in python


I need to do for loops to adjacency matrix in python. My goal is to find the first and second order of neighbours in matrix. I did the matrix with pysal queen contiguity. 1 is neighbour, 0 isn´t a neighbour.Code:

import pandas as pd
import pysal as ps

w = ps.queen_from_shapefile('test.shp')
Wmatrix, ids = w.full()
Wmatrix
W_DataFrame = pd.DataFrame(Wmatrix,columns=["A","B","C","D","E","F",
                                                   "G","H","I","J","K","L",
                                                   "N","M"],
                                        index=["A","B","C","D","E","F",
                                                   "G","H","I","J","K","L",
                                                   "N","M"])

print W_DataFrame

The matrix is:

    A    B    C    D    E    F    G    H    I    J    K    L    N    M
A  0.0  0.0  1.0  0.0  0.0  0.0  1.0  0.0  0.0  0.0  1.0  0.0  0.0  0.0
B  0.0  0.0  0.0  0.0  1.0  0.0  1.0  0.0  1.0  0.0  0.0  0.0  0.0  0.0
C  1.0  0.0  0.0  1.0  0.0  0.0  0.0  1.0  0.0  1.0  1.0  1.0  0.0  1.0
D  0.0  0.0  1.0  0.0  0.0  0.0  0.0  0.0  0.0  1.0  0.0  0.0  1.0  0.0
E  0.0  1.0  0.0  0.0  0.0  0.0  1.0  0.0  1.0  0.0  1.0  0.0  0.0  0.0
F  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  1.0  0.0  0.0  1.0  0.0
G  1.0  1.0  0.0  0.0  1.0  0.0  0.0  0.0  0.0  0.0  1.0  0.0  0.0  0.0
H  0.0  0.0  1.0  0.0  0.0  0.0  0.0  0.0  1.0  0.0  1.0  1.0  0.0  0.0
I  0.0  1.0  0.0  0.0  1.0  0.0  0.0  1.0  0.0  0.0  1.0  1.0  0.0  0.0
J  0.0  0.0  1.0  1.0  0.0  1.0  0.0  0.0  0.0  0.0  0.0  0.0  1.0  1.0
K  1.0  0.0  1.0  0.0  1.0  0.0  1.0  1.0  1.0  0.0  0.0  0.0  0.0  0.0
L  0.0  0.0  1.0  0.0  0.0  0.0  0.0  1.0  1.0  0.0  0.0  0.0  0.0  1.0
N  0.0  0.0  0.0  1.0  0.0  1.0  0.0  0.0  0.0  1.0  0.0  0.0  0.0  0.0
M  0.0  0.0  1.0  0.0  0.0  0.0  0.0  0.0  0.0  1.0  0.0  1.0  0.0  0.0

For example: Position A have 3 first neighbours (C,H,K) and B is neighbour of G, and C is neighbour of B.

How do I for loops to built a dictionary of lists? Such as: {'A': ['C','H','K','G','B'] }


Solution

  • It looks you actually have two tasks here:

    1. convert a pandas dataframe to a dictionary showing first order neighbours
    2. convert that dictionary to a dictionary showing second order neighbours

    For the first task, you can use the to_dict pandas function to convert your dataframe to a dictionary of dictionaries and then filter the sub-dictionaries according to whether their values are 0.0 or 1.0. Using your dataframe df as an example:

    d = {}
    for k, subdict in df.to_dict().items():
        neighbours = []
        for k2, value in subdict.items():
            if value:
                neighbours.append(k2)
        d[k] = neighbours
    

    Now d is a dictionary containing the first order neighbours for each key:

    print(d)
    {'A': ['C', 'G', 'K'],
     'B': ['E', 'G', 'I'],
     'C': ['A', 'D', 'H', 'J', 'K', 'L', 'M'],
     'D': ['C', 'J', 'N'],
     'E': ['B', 'G', 'I', 'K'],
     'F': ['J', 'N'],
     'G': ['A', 'B', 'E', 'K'],
     'H': ['C', 'I', 'K', 'L'],
     'I': ['B', 'E', 'H', 'K', 'L'],
     'J': ['C', 'D', 'F', 'N', 'M'],
     'K': ['A', 'C', 'E', 'G', 'H', 'I'],
     'L': ['C', 'H', 'I', 'M'],
     'N': ['D', 'F', 'J'],
     'M': ['C', 'J', 'L']}
    

    In order to convert this to show the second order neighbours as well you should loop over the values for each key, look up the neighbours for each value and add those to the original list of values.

    def find_second_order_neighbours(d):
        d2 = {} 
        for k, neighbours in d.items(): # loop over the dictionary
            new_neighbours = set(neighbours) # create a temporary set to store all second order neighbours
            for neighbour in neighbours: # loop over the original neighbours
                new_neighbours = (new_neighbours | set(d[neighbour])) - {k} # add all second order neighbours ignoring duplicates (and making sure k is not its own neighbour)
            d2[k] = list(new_neighbours) # update the dictionary to return
        return d2
    
    print(find_second_order_neighbours(d))
    {'A': ['E', 'K', 'G', 'C', 'L', 'J', 'I', 'H', 'M', 'D', 'B'],
     'B': ['E', 'G', 'K', 'L', 'A', 'I', 'H'],
     'C': ['N', 'E', 'K', 'G', 'L', 'A', 'J', 'I', 'H', 'M', 'F', 'D'],
     'D': ['N', 'K', 'C', 'L', 'A', 'J', 'H', 'M', 'F'],
     'E': ['K', 'G', 'C', 'L', 'A', 'I', 'H', 'B'],
     'F': ['N', 'C', 'J', 'M', 'D'],
     'G': ['E', 'K', 'C', 'A', 'I', 'H', 'B'],
     'H': ['E', 'K', 'G', 'C', 'L', 'A', 'J', 'I', 'M', 'D', 'B'],
     'I': ['E', 'K', 'G', 'C', 'L', 'A', 'H', 'M', 'B'],
     'J': ['N', 'K', 'C', 'L', 'A', 'H', 'M', 'F', 'D'],
     'K': ['E', 'G', 'C', 'L', 'A', 'J', 'I', 'H', 'M', 'D', 'B'],
     'L': ['E', 'K', 'C', 'A', 'J', 'I', 'H', 'M', 'D', 'B'],
     'N': ['C', 'J', 'M', 'F', 'D'],
     'M': ['N', 'K', 'C', 'L', 'A', 'J', 'I', 'H', 'D', 'F']}
    

    EXTRA

    If you are interested in more that just the second order neighbours (third order, fourth order, etc.) you can repeatedly call the find_second_order_neighbours function to find the nth order neighbour like so:

    def find_n_order_neighbours(n, d):
        while n > 1:
            d = find_second_order_neighbours(d)
            n -= 1
        return d