<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Creating Graphs in NetworkX
Creating a graph object
Adding nodes and edges
Adding attributes
Loading in several data types
Step1:  ⚠️
Note
Step2:  Adding and Inspecting Attributes
Step3:  Adding Attributes for each existing node
Step4:  Reading in Different Representations of Graphs
Data for graphs and networks comes in many different representations.
Representations
Step5:  Mediating Data Processing through pandas
Often times the data we'll want to use will probably be analyzed beforehand with pandas. Reading in our data to a DataFrame first saves us a bit of time writng code to open the files due to read_csv having sensible defaults around quoted characters and header rows.
Step6:  Adjacency Matrices
A common way of representing graph data is through an adjacency matrix -- often referred to mathematically as A. This data structure is a square, n x n matrix where n = number of nodes. Each column and row in the matrix is a node. For any two nodes, i and j the value at Aij (row i and column j) represents the weight of the edge between nodes i and j.
Step7:  Are the two graphs the same?
Step8:  Gotchas
Step9:  ⚠️ Observation
Step10:  ⚠️  Observation
<ASSISTANT_TASK:>
Python Code:
import csv
import networkx as nx 
import pandas as pd 
import matplotlib.pyplot as plt 
%matplotlib inline
# Create empty graph
G = nx.Graph()
# Add nodes
G.add_node(1)
G.add_nodes_from([2, 3])
G.add_node(4)
G.nodes()
Explanation: Creating Graphs in NetworkX
Creating a graph object
Adding nodes and edges
Adding attributes
Loading in several data types
End of explanation
# add edges
G.add_edge(1, 2)
# get graph info
print(nx.info(G))
nx.draw(G, with_labels=True)
Explanation: ⚠️
Note: In networkx 2.0, several methods now return iterators
For more details see: https://networkx.github.io/documentation/development/reference/migration_guide_from_1.x_to_2.0.html
End of explanation
# add at creation
# nodes
G.add_node(5, favorite_color='blue')
G.add_nodes_from([(6, {'favorite_color' : 'red'}),
                  (7, {'favorite_color' :'purple'})])
# edges
G.add_edge(5, 6, {'relationship' : 'best friends'})
# accessing node attributes
print("Node 5 attributes:", G.node[5])
# accessing edge attributes
print("Edge 5-6 attributes:", G.edge[5][6])
Explanation: Adding and Inspecting Attributes
End of explanation
favorite_foods = {
    1 : 'pizza',
    2 : 'mac and cheese',
    3 : 'balogna sandwich',
    4 : 'pizza',
    5 : 'chocolate',
    6 : 'pizza',
    7 : 'bananas'
}
nx.set_node_attributes(G, 'favorite_food', favorite_foods)
print("Node 4's favorite food is %s" % G.node[4]['favorite_food'])
Explanation: Adding Attributes for each existing node
End of explanation
# what does it look like? 
!head ../data/ga_edgelist.csv
edges = []
with open('../data/ga_edgelist.csv', 'r') as f:
    filereader = csv.reader(f, delimiter=",", quotechar='"')
    next(filereader) # skips header row
    for row in filereader:
            edges.append(row)
edges[0:5]
GA = nx.from_edgelist(edges)
print(nx.info(GA))
Explanation: Reading in Different Representations of Graphs
Data for graphs and networks comes in many different representations.
Representations:
- Edge List
- Adjacency Matrix
- Adjacency List (not covered)
- Incidence Matrix (not covered)
Note: Representations are related to, but distinct from, the storage format. In our examples, we'll be loading our data from text files. You may also have network data stored as JSON, GEXF, or other formats. For more details, check the docs.
Grey's Anatomy Dataset
The dataset we'll look at is a record of all "romantic" encounters between characters on the TV show Grey's Anatomy.
Edge Lists
An edge list is a common way of representing a graph. This representation can be thought of as a list of tuples, where each tuple represents an edge between two of the nodes in your graph. The nodes of the graph can be inferred by taking the set of objects from all tuples.
You can infer/determine whether a graph is directed or weighted from an edge list.
- Weighted: If edges appear more than once, or if an additional weight attribute is added as a 3rd column, the graph is weighted
- Directed: If the "From" and "To" (often seen as "Source" and "Target") of an edge in the list is not arbitrary, it's a directed graph
End of explanation
ga_edges = pd.read_csv('../data/ga_edgelist.csv')
ga_edges.head()
GA = nx.from_pandas_dataframe(ga_edges, source="from", target="to")
# validate info
print(nx.info(GA))
nx.draw(GA, with_labels=True)
Explanation: Mediating Data Processing through pandas
Often times the data we'll want to use will probably be analyzed beforehand with pandas. Reading in our data to a DataFrame first saves us a bit of time writng code to open the files due to read_csv having sensible defaults around quoted characters and header rows.
End of explanation
ga_adj = pd.read_csv('../data/ga_adj.csv', index_col=0)
ga_adj.ix[0:5, 0:5]
GAAdj = nx.from_numpy_matrix(ga_adj.values)
# Numpy matrices don't have labels :(
print(GAAdj.nodes())
label_mapping = dict(zip(GAAdj.nodes(), ga_adj.columns))
GAAdj = nx.relabel_nodes(GAAdj, label_mapping)
nx.draw_spring(GAAdj, with_labels=True)
Explanation: Adjacency Matrices
A common way of representing graph data is through an adjacency matrix -- often referred to mathematically as A. This data structure is a square, n x n matrix where n = number of nodes. Each column and row in the matrix is a node. For any two nodes, i and j the value at Aij (row i and column j) represents the weight of the edge between nodes i and j.
End of explanation
# Easiest, least robust way:
print("Edge List Graph\n", nx.info(GA))
print("\nAdj. Matrix Graph\n", nx.info(GAAdj))
# Fancy math way that checks additional conditions
print("Isomorphic?", nx.is_isomorphic(GA, GAAdj))
Explanation: Are the two graphs the same?
End of explanation
print("'denny' From Edge List Graph:", GA['denny'])
print("'denny' From Adjacency Matrix Graph:", GAAdj['denny'])
Explanation: Gotchas
End of explanation
original_edgelist = sorted(nx.to_edgelist(GA))
adjacency_edgelist = sorted(nx.to_edgelist(GAAdj))
for i, edge in enumerate(original_edgelist):
    adjacency_edge = adjacency_edgelist[i]
    if edge[0] != adjacency_edge[0]:
        print("Sorted Edge Mismatch at edge %s:" % i, edge, adjacency_edge)
        break
Explanation: ⚠️ Observation: Edge weights are inferred from adjacency matrix
End of explanation
nx.write_gexf(GA, '../data/ga_graph.gexf')
Explanation: ⚠️  Observation: Source and Target are ambiguously defined in undirected graphs
Exporting Graphs
We'll export the graph in GEXF (Graph Exchange XML Format).
End of explanation
<END_TASK>