How to create a directed graph?

Viewed 400

I am trying to create a directed graph upon this dataset:

    ID  Link_to Label   Date    Size
0   mary    NaN 0       2020-01-23 1
1   Julie Mirk  1       2020-02-27 12
1   Julie Mark  1       2020-02-27 12
1   Julie Sarah 1       2020-02-27 12
1   Chris Mirk  1       2020-01-26 12
... ... ... ... ... ... ...
50  Mirk    Chris   0   2020-04-29 4
51  Mark    NaN 0       2020-04-29 3
52  Greg    NaN 0       2020-04-27 2
53  Luke    Matt    0       2020-04-08 1
54  Sarah   James   0       2020-04-01 1

To create an undirected graph with my data above, I did:

G = nx.from_pandas_edgelist(df, 'ID', 'Link_to')

d = dict(df.drop_duplicates(subset=['ID'])[['ID']]
           .to_numpy().tolist())

nodes = G.nodes()
plt.figure(figsize=(20,33)) 
pos = nx.draw(G, with_labels=True, 
              nodelist=nodes,
              node_color=[d.get(i,'lightgreen') for i in nodes], 
              node_size=1000) 

I would like to add date information in the graph, in order to create a directed graph: the ID who has the earliest date is the source. So for example: Julie and Mirk are linked together: a directed link from Julie to Mirk should be added.

Another example: Chris and Mirk are connected to each other. However, since Chris has an earliest date compared to ID's than connect Mirk to Chris. If two IDs are linked to each other and they have the same date, they should have one arrows (bidirectional).

How can I figure out how to add Date information in my graph?

2 Answers

From the data you provided (I added an extra line Sarah -> Julie with same Date):

s = """index    ID  Link_to Label   Date    Size
0   mary    NaN 0       2020-01-23 1
1   Julie Mirk  1       2020-02-27 12
1   Julie Mark  1       2020-02-27 12
1   Julie Sarah 1       2020-02-27 12
1   Sarah Julie 1       2020-02-27 12
1   Chris Mirk  1       2020-01-26 12
50  Mirk    Chris   0   2020-04-29 4
51  Mark    NaN 0       2020-04-29 3
52  Greg    NaN 0       2020-04-27 2
53  Luke    Matt    0       2020-04-08 1
54  Sarah   James   0       2020-04-01 1"""
df = pd.read_csv(io.StringIO(re.sub("[ ]+", ",", s)), parse_dates=['Date'])
df = df.dropna().drop(["index", "Label", "Size"], axis=1)

We have the following dataset:

       ID Link_to       Date
1   Julie    Mirk 2020-02-27
2   Julie    Mark 2020-02-27
3   Julie   Sarah 2020-02-27
4   Sarah   Julie 2020-02-27
5   Chris    Mirk 2020-01-26
6    Mirk   Chris 2020-04-29
9    Luke    Matt 2020-04-08
10  Sarah   James 2020-04-01

We can merge the dataframe against itself to inspect which combination should be kept:

c = df.merge(df, how='left', left_on=['ID', 'Link_to'], right_on=['Link_to', 'ID'], suffixes=('', '_r'))
c['Date_r'] = c['Date_r'].fillna(c['Date'])

Assuming you want to keep the earliest relation (eg.: Chris -> Mirk instead of Mirk -> Chris) then the criterion is about:

c['keep'] = c['Date'] <= c['Date_r']

If you intend the converse, just change the inequality relation to >=.

The results looks like:

      ID Link_to       Date   ID_r Link_to_r     Date_r   keep
0  Julie    Mirk 2020-02-27    NaN       NaN 2020-02-27   True
1  Julie    Mark 2020-02-27    NaN       NaN 2020-02-27   True
2  Julie   Sarah 2020-02-27  Sarah     Julie 2020-02-27   True
3  Sarah   Julie 2020-02-27  Julie     Sarah 2020-02-27   True
4  Chris    Mirk 2020-01-26   Mirk     Chris 2020-04-29   True
5   Mirk   Chris 2020-04-29  Chris      Mirk 2020-01-26  False
6   Luke    Matt 2020-04-08    NaN       NaN 2020-04-08   True
7  Sarah   James 2020-04-01    NaN       NaN 2020-04-01   True

And building the Directed Graph is as simple as:

c = c.loc[c['keep'], :]
G = nx.from_pandas_edgelist(c, 'ID', 'Link_to', create_using=nx.DiGraph)

Final results is about:

nx.draw_networkx(G)

enter image description here

It seems to be compliant with your expected output:

  • Single edge are represented;
  • Double edges are handled as follow:
    • Same Dates, keep both edges;
    • Different Dates, keep earliest one.

So I have a theory on which you can work on First convert the Date to the Unix Time Stamp, which is an integer number. You can take a look at the following example.

>>> import time
>>> import datetime
>>> s = "01/12/2011"
>>> time.mktime(datetime.datetime.strptime(s, "%d/%m/%Y").timetuple())
1322697600.0

Now once you have the whole column converted to a Unix timestamp. What you can do now is simply greater than or less than ('<', '>','=') operations. In other words, If the unix timestamp of A is less than Unix timestamp of B then it means you have a directed edge from A to B.

Now Once you know that there is a directed edge from A to B you can use

G.add_edge('A', 'B')

You can check more about the Directed Graph here

Related