使用带量词的命题形式的节点调整网图中的字体大小?

Adjust fontsize in netgraph with nodes in form of proposition with quantifiers?

我正在使用 netgraph 可视化我的 networkx 图。我现在的问题是,公式的可视化以自然的方式不如我预期的那么漂亮。这是我的示例图:

import networkx as nx
from netgraph import Graph # pip install netgraph
import matplotlib.pyplot as plt

node_labels = {1: 'p→q', 2: '¬q', 3: '¬ (¬p)', 4: '¬p', 5: '¬p∧ ¬ (¬p)', 6: 'p', 7: 'q', 8: 'q∧ ¬q', 9: '¬p'}
color_map = {1: 'red', 2: 'red', 3: 'red', 4: 'red', 5: 'lightblue', 6: 'lightblue', 7: 'lightblue', 8: 'lightblue', 9: 'blue'}
edge_labels = {(3, 5): '∧I', (4, 5): '∧I', (4, 6): '¬E', (5, 6): '¬E', (1, 7): '→E', (6, 7): '→E', (2, 8): '∧I', (7, 8): '∧I', (8, 9): '¬E', (3, 9): '¬E'}

graph = nx.from_edgelist(edge_labels, nx.DiGraph())


Graph(graph, node_labels=node_labels, edge_labels=edge_labels,
      node_color=color_map, node_edge_color=color_map, arrows=True)

plt.show()

输出(直接/放大后):

:

我现在的问题:

Plotly 有一个 very nice example of network graph 可以很容易地重新适应您的需要。

这是您的问题的答案。

# Packages import
import networkx as nx
import plotly.graph_objs as go
# Your input data
node_labels = {1: 'p→q', 2: '¬q', 3: '¬ (¬p)', 4: '¬p', 5: '¬p∧ ¬ (¬p)', 6: 'p', 7: 'q', 8: 'q∧ ¬q', 9: '¬p'}
color_map = {1: 'red', 2: 'red', 3: 'red', 4: 'red', 5: 'lightblue', 6: 'lightblue', 7: 'lightblue', 8: 'lightblue', 9: 'blue'}
edge_labels = {(3, 5): '∧I', (4, 5): '∧I', (4, 6): '¬E', (5, 6): '¬E', (1, 7): '→E', (6, 7): '→E', (2, 8): '∧I', (7, 8): '∧I', (8, 9): '¬E', (3, 9): '¬E'}

# Create DiGraph
G=nx.DiGraph()
# Add nodes and edges
G.add_nodes_from(list(node_labels.keys()), weight=15)
G.add_edges_from(list(edge_labels.keys()))
# Create Positions
pos = nx.planar_layout(G)  # nx.random_layout(G), ...
# Create edges for plot and edge annotations
edge_x = []
edge_y = []
edge_label_x = []
edge_label_y = []
for edge in G.edges:
    x0, y0 = pos[edge[0]][0], pos[edge[0]][1]
    x1, y1 = pos[edge[1]][0], pos[edge[1]][1]
    edge_x.append(x0)
    edge_x.append(x1)
    edge_x.append(None)
    edge_y.append(y0)
    edge_y.append(y1)
    edge_y.append(None)
    edge_label_x.append((x0+x1)/2)
    edge_label_y.append((y0+y1)/2)
# Make go.Scatter for edges
edge_trace = go.Scatter(
    x=edge_x, y=edge_y,
    line=dict(width=0.5, color='#888'),
    hoverinfo='none',
    mode='lines'
)

# Create nodes for plot
node_x = []
node_y = []
node_color = []
for node in G.nodes:
    x, y = pos[node][0], pos[node][1]
    node_x.append(x)
    node_y.append(y)
    node_color.append(color_map[node])
# Make go.Scatter for nodes
node_trace = go.Scatter(
    x=node_x, y=node_y,
    mode='markers+text',
    hoverinfo='text',
    text=list(node_labels.values()),
    textposition="middle center",  # ['top left', 'top center', 'top right', 'middle left',
                                   # 'middle center', 'middle right', 'bottom left', 
                                   # 'bottom center', 'bottom right']
    textfont=dict(
        family="sans serif",
        size=10,  # <-- Here you change the node text size
        color="black"
    ),
    marker=dict(
        showscale=False,
        color=node_color,
        size=46,
        line_width=1
    )
)
# Make edge annotations
edge_annotations = []
for label, x, y in zip(list(edge_labels.values()), edge_label_x, edge_label_y):
    edge_annotations.append(
        dict(
            x=x, y=y,
            showarrow=False,
            hovertext='none',
            ax=0,
            ay=0,
            bgcolor="white",
            opacity=0.85,
            text=label,
            font=dict(
                family="sans serif",
                size=14,
                color="black",
            )
        )
    )

# Make go.Figure
fig = go.Figure(
    data=[edge_trace, node_trace],
    layout=go.Layout(
        paper_bgcolor='rgba(0,0,0,0)',
        plot_bgcolor='rgba(0,0,0,0)',
        title='<br>Network graph',
        titlefont_size=16,
        showlegend=False,
        hovermode='closest',
        xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
        yaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
        autosize=False,
        width=700,
        height=700,
        margin=dict(l=50, r=50, b=100, t=100, pad=4),
    )
)
# Add edge annotations to go.Figure
for edge_annotation in edge_annotations:
    fig.add_annotation(edge_annotation)
# Show go.Figure
fig.show()

导致:

编辑:

使用 Plotly 添加边缘方向有点棘手(). Fortunately there is a github project 我们可以从中借用一个函数,通过考虑节点大小向边缘添加箭头。

现在的代码是:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri May 15 11:45:07 2020
@author: aransil
"""

import math

# Start and end are lists defining start and end points
# Edge x and y are lists used to construct the graph
# arrowAngle and arrowLength define properties of the arrowhead
# arrowPos is None, 'middle' or 'end' based on where on the edge you want the arrow to appear
# arrowLength is the length of the arrowhead
# arrowAngle is the angle in degrees that the arrowhead makes with the edge
# dotSize is the plotly scatter dot size you are using (used to even out line spacing when you have a mix of edge lengths)
def addEdge(start, end, edge_x, edge_y, lengthFrac=1, arrowPos = None, arrowLength=0.025, arrowAngle = 30, dotSize=20):

    # Get start and end cartesian coordinates
    x0, y0 = start
    x1, y1 = end

    # Incorporate the fraction of this segment covered by a dot into total reduction
    length = math.sqrt( (x1-x0)**2 + (y1-y0)**2 )
    dotSizeConversion = .0565/20 # length units per dot size
    convertedDotDiameter = dotSize * dotSizeConversion
    lengthFracReduction = convertedDotDiameter / length
    lengthFrac = lengthFrac - lengthFracReduction

    # If the line segment should not cover the entire distance, get actual start and end coords
    skipX = (x1-x0)*(1-lengthFrac)
    skipY = (y1-y0)*(1-lengthFrac)
    x0 = x0 + skipX/2
    x1 = x1 - skipX/2
    y0 = y0 + skipY/2
    y1 = y1 - skipY/2

    # Append line corresponding to the edge
    edge_x.append(x0)
    edge_x.append(x1)
    edge_x.append(None) # Prevents a line being drawn from end of this edge to start of next edge
    edge_y.append(y0)
    edge_y.append(y1)
    edge_y.append(None)

    # Draw arrow
    if not arrowPos == None:

        # Find the point of the arrow; assume is at end unless told middle
        pointx = x1
        pointy = y1

        eta = math.degrees(math.atan((x1-x0)/(y1-y0))) if y1!=y0 else 90.0

        if arrowPos == 'middle' or arrowPos == 'mid':
            pointx = x0 + (x1-x0)/2
            pointy = y0 + (y1-y0)/2

        # Find the directions the arrows are pointing
        signx = (x1-x0)/abs(x1-x0) if x1!=x0 else +1    #verify this once
        signy = (y1-y0)/abs(y1-y0) if y1!=y0 else +1    #verified

        # Append first arrowhead
        dx = arrowLength * math.sin(math.radians(eta + arrowAngle))
        dy = arrowLength * math.cos(math.radians(eta + arrowAngle))
        edge_x.append(pointx)
        edge_x.append(pointx - signx**2 * signy * dx)
        edge_x.append(None)
        edge_y.append(pointy)
        edge_y.append(pointy - signx**2 * signy * dy)
        edge_y.append(None)

        # And second arrowhead
        dx = arrowLength * math.sin(math.radians(eta - arrowAngle))
        dy = arrowLength * math.cos(math.radians(eta - arrowAngle))
        edge_x.append(pointx)
        edge_x.append(pointx - signx**2 * signy * dx)
        edge_x.append(None)
        edge_y.append(pointy)
        edge_y.append(pointy - signx**2 * signy * dy)
        edge_y.append(None)


    return edge_x, edge_y
# Packages import
import networkx as nx
import plotly.graph_objs as go
# Your input data
node_labels = {1: 'p→q', 2: '¬q', 3: '¬ (¬p)', 4: '¬p', 5: '¬p∧ ¬ (¬p)', 6: 'p', 7: 'q', 8: 'q∧ ¬q', 9: '¬p'}
color_map = {1: 'red', 2: 'red', 3: 'red', 4: 'red', 5: 'lightblue', 6: 'lightblue', 7: 'lightblue', 8: 'lightblue', 9: 'blue'}
edge_labels = {(3, 5): '∧I', (4, 5): '∧I', (4, 6): '¬E', (5, 6): '¬E', (1, 7): '→E', (6, 7): '→E', (2, 8): '∧I', (7, 8): '∧I', (8, 9): '¬E', (3, 9): '¬E'}

# General controls on the figure
NODE_SIZE = 46
LINE_WIDTH = 0.5
LINE_COLOR = '#888888'
FONT_FAMILY = 'serif'

# Create DiGraph
G=nx.DiGraph()
# Add nodes and edges
G.add_nodes_from(list(node_labels.keys()), weight=15)
G.add_edges_from(list(edge_labels.keys()))
# Create Positions
pos = nx.planar_layout(G)  # nx.random_layout(G), ...
pos[4][0] = -0.7  # <-- Changing the position of node 4 for readability
pos[9][0] = 0.9  # <-- Changing the position of node 9 for readability
for node in G.nodes:
    G.nodes[node]['pos'] = list(pos[node])

# Create Plot
# Create edges for plot and edge annotations
edge_x = []
edge_y = []
edge_label_x = []
edge_label_y = []
for edge in G.edges:
    start = G.nodes[edge[0]]['pos']
    end = G.nodes[edge[1]]['pos']
    edge_label_x.append((start[0]+end[0])/2)
    edge_label_y.append((start[1]+end[1])/2)
    edge_x, edge_y = addEdge(start, end, edge_x, edge_y, .95, 'end', .03, 20, NODE_SIZE)
# Make go.Scatter for edges
edge_trace = go.Scatter(
    x=edge_x, y=edge_y,
    mode='lines',
    line=dict(width=LINE_WIDTH, color=LINE_COLOR),
    hoverinfo='none',
)

# Create nodes for plot
node_x = []
node_y = []
node_color = []
for node in G.nodes:
    x, y = G.nodes[node]['pos']
    node_x.append(x)
    node_y.append(y)
    node_color.append(color_map[node])
# Make go.Scatter for nodes
node_trace = go.Scatter(
    x=node_x, y=node_y,
    mode='markers+text',
    hoverinfo='text',
    text=list(node_labels.values()),
    textposition="middle center",  # ['top left', 'top center', 'top right', 'middle left',
                                   # 'middle center', 'middle right', 'bottom left', 
                                   # 'bottom center', 'bottom right']
    textfont=dict(
        family=FONT_FAMILY,
        size=10,
        color="black"
    ),
    marker=dict(
        showscale=False,
        color=node_color,
        size=NODE_SIZE,
        line_width=1
    )
)
# Make edge annotations
edge_annotations = []
for label, x, y in zip(list(edge_labels.values()), edge_label_x, edge_label_y):
    edge_annotations.append(
        dict(
            x=x, y=y,
            showarrow=False,
            hovertext='none',
            ax=0,
            ay=0,
            bgcolor="white",
            opacity=0.95,
            text=label,
            font=dict(
                family=FONT_FAMILY,
                size=14,
                color="black",
            )
        )
    )

# Make go.Figure
fig = go.Figure(
    data=[edge_trace, node_trace],
    layout=go.Layout(
        paper_bgcolor='rgba(0,0,0,0)',
        plot_bgcolor='rgba(0,0,0,0)',
        title='<br>Network graph',
        titlefont_size=16,
        showlegend=False,
        hovermode='closest',
        xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
        yaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
        autosize=False,
        width=700,
        height=700,
        margin=dict(l=50, r=50, b=100, t=100, pad=4),
    )
)
# Add edge annotations to go.Figure
for edge_annotation in edge_annotations:
    fig.add_annotation(edge_annotation)
# Show go.Figure
fig.show()

导致:

默认情况下,iff 节点标签绘制在节点顶部(即没有偏移),netgraph 缩放节点标签字体大小,以便所有标签适合其各自的节点艺术家。但是,可以通过明确指定字体大小来覆盖此行为,例如使用 node_label_fontdict(size=20).

可以通过设置 edge_label_rotate = False 标志来关闭边缘标签的旋转。

#!/usr/bin/env python
# coding: utf-8

import matplotlib.pyplot as plt
import networkx as nx

from netgraph import Graph # pip install netgraph

node_labels = {1: 'p→q', 2: '¬q', 3: '¬ (¬p)', 4: '¬p', 5: '¬p∧ ¬ (¬p)', 6: 'p', 7: 'q', 8: 'q∧ ¬q', 9: '¬p'}
color_map = {1: 'red', 2: 'red', 3: 'red', 4: 'red', 5: 'lightblue', 6: 'lightblue', 7: 'lightblue', 8: 'lightblue', 9: 'blue'}
edge_labels = {(3, 5): '∧I', (4, 5): '∧I', (4, 6): '¬E', (5, 6): '¬E', (1, 7): '→E', (6, 7): '→E', (2, 8): '∧I', (7, 8): '∧I', (8, 9): '¬E', (3, 9): '¬E'}

graph = nx.from_edgelist(edge_labels, nx.DiGraph())

Graph(graph, node_layout='dot',
      node_labels=node_labels, node_label_fontdict=dict(size=21),
      edge_labels=edge_labels, edge_label_fontdict=dict(size=14), edge_label_rotate=False,
      node_color=color_map, node_edge_color=color_map, arrows=True
)

plt.show()