Как создать ориентированный граф?

#python #pandas #networkx

#python #pandas #networkx

Вопрос:

Я пытаюсь создать ориентированный граф для этого набора данных:

     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
  

Чтобы создать неориентированный граф с моими данными выше, я сделал:

 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) 
  

Я хотел бы добавить информацию о дате в график, чтобы создать ориентированный график: идентификатор, у которого самая ранняя дата, является источником.
Итак, например: Julie и Mirk связаны друг с другом: следует добавить направленную ссылку от Julie к Mirk.

Другой пример: Крис и Мирк связаны друг с другом. Однако, поскольку у Chris есть более ранняя дата по сравнению с идентификаторами, чем подключить Mirk к Chris. Если два идентификатора связаны друг с другом и имеют одинаковую дату, у них должна быть одна стрелка (двунаправленная).

Как я могу выяснить, как добавить информацию о дате в мой график?

Ответ №1:

Из предоставленных вами данных (я добавил дополнительную строку Sarah -> Julie с тем же 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)
  

У нас есть следующий набор данных:

        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
  

Мы можем объединить фрейм данных с самим собой, чтобы проверить, какую комбинацию следует сохранить:

 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'])
  

Предполагая, что вы хотите сохранить самое раннее отношение (например.: Chris -> Mirk вместо Mirk -> Chris ), тогда критерий равен примерно:

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

Если вы намереваетесь сделать обратное, просто измените отношение неравенства на >= .

Результаты выглядят так:

       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
  

И построение ориентированного графа так же просто, как:

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

Окончательные результаты примерно:

 nx.draw_networkx(G)
  

введите описание изображения здесь

Похоже, он соответствует вашему ожидаемому результату:

  • Представлены отдельные ребра;
  • Двойные ребра обрабатываются следующим образом:
    • Одинаковые даты, сохраняйте оба ребра;
    • Разные даты, сохраняйте самую раннюю.

Ответ №2:

Итак, у меня есть теория, над которой вы можете поработать, сначала преобразуя дату в метку времени Unix, которая является целым числом. Вы можете взглянуть на следующий пример.

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

Теперь, когда у вас есть весь столбец, преобразованный в временную метку Unix. То, что вы можете сделать сейчас, просто больше или меньше, чем (‘<‘, ‘>’,’=’) операции. Другими словами, если временная метка unix для A меньше, чем временная метка Unix для B, то это означает, что у вас есть направленное ребро от A до B.

Теперь, когда вы знаете, что существует направленное ребро от A до B, вы можете использовать

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

Вы можете узнать больше о направленном графике здесь