Draw common friends connections of three people us

2019-06-14 16:34发布

问题:

I have three people (with option of adding more people) and I want to show each person friends list as a circle. So, 1 circle per person. Next, I want to add edges(connections) if two people have same friend.

Mike_friends = ['Al', 'Niki', 'Silvia', 'Anna', 'Matt', 'Gia', 'Nick', 'Maud', 'Sarah', 'Lisa', 'Kelvin']
Alex_friends = ['Harvey', 'Steve', 'Michael', 'Maud', 'Al', 'Kam', 'Hank']
Stephen_friends = ['Lisa','Rosie','Mango','Kate','Nate','Maud','Kelvin','Elvis','Arstad','Jesus','Johan','Jay','Gia','Niki','Harvey','Paul','Mike','Alex','Anna']
import networkx as nx
import matplotlib.pyplot as plt

def link_networks(N1, N2, N3, N4=None, N5=None, N6=None, N7=None, N8=None, N9=None):
    G = nx.Graph()
    for item1 in N1:
        for item2 in N2:
            for item3 in N3:
                if item1 in N1 == item2 in N2:
                    G.add_edge(item1, item2)
                elif item1 in N1 == item3 in N3:
                    G.add_edge(item1, item3)
                elif item2 in N2 == item3 in N3:
                    G.add_edge(item2, item3)
link_networks(Mike_friends, Alex_friends, Stephen_friends)
plt.figure(figsize = (20, 10))
mytitle = "friend circle connections"
plt.title(mytitle, fontsize=40)
nx.draw_networkx(G, font_size=10, node_size=2000, alpha=0.6)
plt.savefig("friend_circles.pdf")

The problem: I get empty figure, 3 circles with connections do not show up. Thanks for help. Removed 'Return G'.

回答1:

Here is one way of doing it. First you find how many common friends have Mike, Alex and Stephen. I do this by using set; here's the example for Mike and Alex.

M_A_common = len(set(Mike_friends) & set(Alex_friends))

The nodes in my graph are called Mike, Alex and Stephen. Below I set the edge Mike -- Alex so it has the value of M_A_common.

G.add_edge('Mike', 'Alex', common_friends=M_A_common)

Here is the full example with all friends where I draw the network with nx.draw and the edge labels with nx.draw_networkx_edge_labels.

import networkx as nx
import matplotlib.pyplot as plt

Mike_friends = ['Al', 'Niki', 'Silvia', 'Anna', 'Matt', 'Gia', 'Nick', 'Maud', 'Sarah', 'Lisa', 'Kelvin']
Alex_friends = ['Harvey', 'Steve', 'Michael', 'Maud', 'Al', 'Kam', 'Hank']
Stephen_friends = ['Lisa','Rosie','Mango','Kate','Nate','Maud','Kelvin','Elvis','Arstad','Jesus','Johan','Jay','Gia','Niki','Harvey','Paul','Mike','Alex','Anna']

G = nx.Graph()
G.add_node('Mike')
G.add_node('Alex')
G.add_node('Stephen')

M_A_common = len(set(Mike_friends) & set(Alex_friends))  # number of common friends
M_S_common = len(set(Mike_friends) & set(Stephen_friends))  # number of common friends
S_A_common = len(set(Stephen_friends) & set(Alex_friends))  # number of common friends

G.add_edge('Mike', 'Alex', common_friends=M_A_common)
G.add_edge('Mike', 'Stephen', common_friends=M_S_common)
G.add_edge('Stephen', 'Alex', common_friends=S_A_common)

pos = nx.circular_layout(G)
nx.draw(G, pos, node_size=2000, with_labels=True)
edge_labels = nx.get_edge_attributes(G, 'common_friends')
nx.draw_networkx_edge_labels(G, pos, labels=edge_labels)
plt.show()

Instead of writing the number of common friends you could e.g. show the amount of common friends with edge thickness.

Of course, with more friends you will have more combinations so you'll probably want to automate the calculation of common friends.

Also, if you want multiple edges for each common friend you need to use MultiGraph instead of Graph. For drawing multiple edges take a look here.

EDIT:

To add custom node labels (e.g. node name + number of total friends) for the code above you could use nx.draw_networkx_labels like this:

pos = nx.circular_layout(G)
nx.draw(G, pos, node_size=2000, with_labels=False)
node_labels = {
    'Mike': 'Mike (' + str(len(Mike_friends)) + ' total)',
    'Alex': 'Alex (' + str(len(Alex_friends)) + ' total)',
    'Stephen': 'Stephen (' + str(len(Stephen_friends)) + ' total)'
}
nx.draw_networkx_labels(G, pos, labels=node_labels)
edge_labels = nx.get_edge_attributes(G, 'common_friends')
nx.draw_networkx_edge_labels(G, pos, labels=edge_labels)
plt.show()

If you had 20 friends the code above would get really messy. Below is a more elegant approach where I store the nodes and their friends in a dictionary called friends.

To get all possible edges I use itertools.combinations

import networkx as nx
import matplotlib.pyplot as plt
from itertools import combinations

G = nx.Graph()
friends = {
    'Mike': ['Al', 'Niki', 'Silvia', 'Anna', 'Matt', 'Gia', 'Nick', 'Maud', 'Sarah', 'Lisa', 'Kelvin'],
    'Alex': ['Harvey', 'Steve', 'Michael', 'Maud', 'Al', 'Kam', 'Hank'],
    'Stephen': ['Lisa','Rosie','Mango','Kate','Nate','Maud','Kelvin','Elvis','Arstad','Jesus','Johan','Jay','Gia','Niki','Harvey','Paul','Mike','Alex','Anna']
}

persons = list(friends.keys())  # Mike, Alex, Stephen
G.add_nodes_from(persons)
combos = list(combinations(persons, 2))  # all 2-combinations of `persons`

for edge in combos:
    node_1, node_2 = edge[0], edge[1]
    common = len(set(friends[node_1]) & set(friends[node_2]))  # number of common friends
    G.add_edge(node_1, node_2, common_friends=common)

node_labels = {person: person + ' (' + str(len(friends[person])) + ' total)' for person in persons}
edge_labels = nx.get_edge_attributes(G, 'common_friends')

pos = nx.circular_layout(G)
nx.draw(G, pos, node_size=2000, with_labels=False)
nx.draw_networkx_labels(G, pos, labels=node_labels)
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
plt.show()

Output:

EDIT 2:

I corrected a line in my code that now gives different edge labels (only values without keys for edge_labels).