Улучшение макета графа Python NetworkX
У меня есть некоторые проблемы с визуализацией графиков, созданных с помощью python-networkx, я хочу уменьшить помехи и регулировать расстояние между узлами (я также пробовал spring_layout, он просто размещает узлы в эллиптической форме). Пожалуйста, порекомендуйте.
Части кода:
nx.draw_networkx_edges(G, pos, edgelist=predges, edge_color='red', arrows=True)
nx.draw_networkx_edges(G, pos, edgelist=black_edges, arrows=False, style='dashed')
# label fonts
nx.draw_networkx_labels(G,pos,font_size=7,font_family='sans-serif')
nx.draw_networkx_edge_labels(G,pos,q_list,label_pos=0.3)
5 ответов
В networkx стоит проверить алгоритмы рисования графа, предоставляемые graphviz через nx.graphviz_layout
,
У меня был хороший успех с neato
но другие возможные входы
dot
- "иерархические" или слоистые рисунки ориентированных графов. Этот инструмент используется по умолчанию, если ребра имеют направленность.neato
- Макеты "весенней модели". Это инструмент по умолчанию, который используется, если график не слишком большой (около 100 узлов), и вы больше ничего о нем не знаете. Neato пытается минимизировать глобальную энергетическую функцию, которая эквивалентна статистическое многомерное масштабирование.fdp
- Макеты "весенней модели" похожи на макеты Neato, но делают это, уменьшая силы, а не работая с энергией.sfdp
- многомасштабная версия fdp для верстки больших графов.twopi
- радиальные схемы, после Грэма Уилса 97. Узлы размещаются на концентрических окружностях в зависимости от их расстояния от данного корневого узла.circo
- круговая схема, после Six и Tollis 99, Kauffman и Wiese 02. Это подходит для определенных диаграмм множественных циклических структур, таких как определенные телекоммуникационные сети.
В общем, рисование графиков является сложной проблемой. Если этих алгоритмов недостаточно, вам придется написать свой собственный или иметь отдельные части рисования.
Я обнаружил, что это полезно для быстрой визуализации данных взаимодействия, полученных в виде файла CSV из PostgreSQL. [Вывод ниже переформатирован для удобства чтения.]
## PSQL ['DUMMY' DATA]:
[interactions_practice]# \copy (SELECT gene_1, gene_2 FROM interactions
WHERE gene_1 in (SELECT gene_2 FROM interactions))
TO '/tmp/a.csv' WITH CSV -- << note: no terminating ";" for this query
## BASH:
[victoria@victoria ~]$ cat /tmp/a.csv
APC,TP73
BARD1,BRCA1
BARD1,ESR1
BARD1,KRAS2
BARD1,SLC22A18
BARD1,TP53
BRCA1,BRCA2
BRCA1,CHEK2
BRCA1,MLH1
BRCA1,PHB
BRCA2,CHEK2
BRCA2,TP53
CASP8,ESR1
CASP8,KRAS2
CASP8,PIK3CA
CASP8,SLC22A18
CDK2,CDKN1A
CHEK2,CDK2
ESR1,BRCA1
ESR1,KRAS2
ESR1,PPM1D
ESR1,SLC22A18
KRAS2,BRCA1
MLH1,CHEK2
MLH1,PMS2
PIK3CA,BRCA1
PIK3CA,ESR1
PIK3CA,RB1CC1
PIK3CA,SLC22A18
PMS2,TP53
PTEN,BRCA1
PTEN,MLH3
RAD51,BRCA1
RB1CC1,SLC22A18
SLC22A18,BRCA1
TP53,PTEN
## PYTHON 3.5 VENV (ANACONDA):
>>> import networkx as nx
>>> import pylab as plt
>>> G = nx.read_edgelist("/tmp/a.csv", delimiter=",")
>>> G.edges()
[('CDKN1A', 'CDK2'), ('MLH3', 'PTEN'), ('TP73', 'APC'), ('CHEK2', 'MLH1'),
('CHEK2', 'BRCA2'), ('CHEK2', 'CDK2'), ('CHEK2', 'BRCA1'), ('BRCA2', 'TP53'),
('BRCA2', 'BRCA1'), ('KRAS2', 'CASP8'), ('KRAS2', 'ESR1'), ('KRAS2', 'BRCA1'),
('KRAS2', 'BARD1'), ('PPM1D', 'ESR1'), ('BRCA1', 'PHB'), ('BRCA1', 'ESR1'),
('BRCA1', 'PIK3CA'), ('BRCA1', 'PTEN'), ('BRCA1', 'MLH1'), ('BRCA1', 'SLC22A18'),
('BRCA1', 'BARD1'), ('BRCA1', 'RAD51'), ('CASP8', 'ESR1'), ('CASP8', 'SLC22A18'),
('CASP8', 'PIK3CA'), ('TP53', 'PMS2'), ('TP53', 'PTEN'), ('TP53', 'BARD1'),
('PMS2', 'MLH1'), ('PIK3CA', 'SLC22A18'), ('PIK3CA', 'ESR1'), ('PIK3CA', 'RB1CC1'),
('SLC22A18', 'ESR1'), ('SLC22A18', 'RB1CC1'), ('SLC22A18', 'BARD1'), ('BARD1', 'ESR1')]
>>> G.number_of_edges()
36
>>> G.nodes()
['CDKN1A', 'MLH3', 'TP73', 'CHEK2', 'BRCA2', 'KRAS2', 'CDK2', 'PPM1D', 'BRCA1',
'CASP8', 'TP53', 'PMS2', 'RAD51', 'PIK3CA', 'MLH1', 'SLC22A18', 'BARD1', 'PHB', 'APC', 'ESR1', 'RB1CC1', 'PTEN']
>>> G.number_of_nodes()
22
>>> from networkx.drawing.nx_agraph import graphviz_layout
>>> ## nx.draw(G, pos=graphviz_layout(G))
## DUE TO AN UNIDENTIFIED BUG, I GET THIS ERROR THE FIRST TIME RUNNING THIS
## COMMAND; JUST RE-RUN IT:
>>> nx.draw(G, pos=graphviz_layout(G), node_size=1200, node_color='lightblue',
linewidths=0.25, font_size=10, font_weight='bold', with_labels=True)
QGtkStyle could not resolve GTK. Make sure you have installed the proper libraries.
>>> nx.draw(G, pos=graphviz_layout(G), node_size=1200, node_color='lightblue',
linewidths=0.25, font_size=10, font_weight='bold', with_labels=True)
>>> plt.show() ## plot1.png [opens in matplotlib popup window] attached
Трудно уменьшить скопление на этих статических графиках networkx / matplotlib; один из обходных путей - увеличить размер рисунка, в соответствии с этим Stackru Q/A: изображение графика с высоким разрешением с использованием NetworkX и Matplotlib:
>>> plt.figure(figsize=(20,14))
<matplotlib.figure.Figure object at 0x7f1b65ea5e80>
>>> nx.draw(G, pos=graphviz_layout(G), node_size=1200, node_color='lightblue',
linewidths=0.25, font_size=10, font_weight='bold', with_labels=True, dpi=1000)
>>> plt.show() ## plot2.png attached
## RESET OUTPUT FIGURE SIZE TO SYSTEM DEFAULT:
>>> plt.figure()
<matplotlib.figure.Figure object at 0x7f1b454f1588>
Бонус - кратчайший путь:
>>> nx.dijkstra_path(G, 'CDKN1A', 'MLH3')
['CDKN1A', 'CDK2', 'CHEK2', 'BRCA1', 'PTEN', 'MLH3']
У вас есть много данных на вашем графике, поэтому будет трудно удалить беспорядок.
Я предлагаю вам использовать любой стандартный макет. Вы сказали, что использовали spring_layout
, Я предлагаю вам попробовать еще раз, но на этот раз, используя weight
атрибут при добавлении ребер.
Например:
import networkx as nx
G = nx.Graph();
G.add_node('A')
G.add_node('B')
G.add_node('C')
G.add_node('D')
G.add_edge('A','B',weight=1)
G.add_edge('C','B',weight=1)
G.add_edge('B','D',weight=30)
pos = nx.spring_layout(G,scale=2)
nx.draw(G,pos,font_size=8)
plt.show()
Дополнительно вы можете использовать параметр scale
увеличить глобальное расстояние между узлами.
Чтобы ответить на ваш вопрос о том, как регулировать расстояние между узлами, я остановлюсь на ответе Хукеда:
Если вы рисуете граф через бэкэнд Graphviz и когда вы затем используете fdp
алгоритм, вы можете настроить расстояние между узлами по атрибутуребраlen
,
Вот пример кода, как нарисовать график G
и сохраните в файле Graphviz gvfile
с большим расстоянием между узлами (расстояние по умолчанию для fdp
является 0.3
):
A = nx.to_agraph(G)
A.edge_attr.update(len=3)
A.write(gv_file_name)
Два комментария:
- Обычно желательно настроить
len
с количеством узлов в графе. len
атрибут распознается толькоfdp
а такжеneato
алгоритм, но не, например,sfdp
алгоритм.
Итак, это может быть бесполезно для исходного вопроса, но, возможно, для будущих искателей:
Решение может быть
pos = nx.nx_agraph.graphviz_layout(G)
nx.draw(G, pos=pos)
Подумайте об установкеagraph
сначала сpip install agraph
.(Решение сnx.nx_pydot.graphviz_layout(G)
теперь будет амортизироваться в соответствии с networkx.)
Надеюсь, поможет :)