Beispiel #1
0
def get_component_mask(G, test_split=0.0):
    assert 0 <= test_split <= 1

    g = dgl.to_networkx(G).to_undirected()
    num_nodes = len(g.nodes())
    train_mask = np.zeros(num_nodes)
    train_comp = set()
    test_mask = None

    for comp in nx.connected_components(g):
        if len(comp) > len(train_comp):
            train_comp = comp

    for i in train_comp:
        train_mask[i] = 1

    assert sum(train_mask) == len(train_comp)

    val_mask = 1 - train_mask

    if test_split:
        val_idx = np.where(val_mask == 1)[0]
        test_idx = np.random.choice(val_idx, int(test_split * num_nodes))
        test_mask = val_mask[test_idx]
        val_mask -= test_mask

    return train_mask, val_mask, test_mask
    def _visualize(self, subgraph, nlabel_mapping=None, title=""):
        num_classes = int(torch.max(self.g.ndata['label']).item()) + 1
        nx_g = dgl.to_networkx(subgraph, node_attrs=['label', ExplainerTags.ORIGINAL_ID])
        mapping = {i: nx_g.nodes[i][ExplainerTags.ORIGINAL_ID].item() for i in nx_g.nodes.keys()}
        node_labels = [nx_g.nodes[i]['label'].item() for i in nx_g.nodes.keys()]

        cmap = plt.get_cmap('cool', num_classes)
        cmap.set_under('gray')

        node_kwargs = {'node_size': 400, 'cmap': cmap, 'node_color': node_labels, 'vmin': 0, 'vmax': num_classes-1}
        label_kwargs = {'labels': mapping, 'font_size': 10}

        pos = nx.spring_layout(nx_g)
        ax = plt.gca()
        for source, target, data in nx_g.edges(data=True):
            ax.annotate(
                '', xy=pos[target], xycoords='data', xytext=pos[source],
                textcoords='data', arrowprops=dict(
                    arrowstyle="->",
                    alpha=1.0,  # max(data['att'], 0.1), # TODO - change for transparent visualization mode
                    shrinkA=sqrt(node_kwargs['node_size']) / 2.0,
                    shrinkB=sqrt(node_kwargs['node_size']) / 2.0,
                    connectionstyle="arc3,rad=0.1",
                ))
        nx.draw_networkx_labels(nx_g, pos, **label_kwargs)
        nx.draw_networkx_nodes(nx_g, pos, **node_kwargs)
        if nlabel_mapping is None:
            nlabel_mapping = {i: i for i in range(num_classes)}
        patch_list = [mpatches.Patch(color=cmap(i), label=nlabel_mapping[i]) for i in range(num_classes)]
        ax.legend(handles=patch_list, title="Label", loc=1)
        if title is not None:
            plt.title(title)
        plt.show()
Beispiel #3
0
def dgl_to_nx(graph, edge_map):
    g = dgl.to_networkx(graph, edge_attrs=['one_hot'])
    edge_map_r = {v: k for k, v in edge_map.items()}
    nx.set_edge_attributes(
        g, {(n1, n2): edge_map_r[d['one_hot'].item()]
            for n1, n2, d in g.edges(data=True)}, 'label')
    return g
Beispiel #4
0
def add_clustering_coefficients_feature(dataset):
    for g, _ in dataset:
        nx_g = dgl.to_networkx(dgl.to_homogeneous(g))
        # MultiDiGraph -> Graph
        nx_g = nx.Graph(nx_g)
        cc = torch.tensor(list(nx.clustering(nx_g).values())).view([-1, 1])
        g.ndata['feat'] = torch.cat([g.ndata['feat'], cc], dim=1)
    return dataset
Beispiel #5
0
def dgl_to_nx(graph, edge_map):
    # find better way to do this..want to be able to launch without torch installed
    import torch
    import dgl
    g = dgl.to_networkx(graph, edge_attrs=['one_hot'])
    edge_map_r = {v: k for k, v in edge_map.items()}
    nx.set_edge_attributes(
        g, {(n1, n2): edge_map_r[d['one_hot'].item()]
            for n1, n2, d in g.edges(data=True)}, 'label')
    return g
Beispiel #6
0
def dgl_to_nx(g_dgl, edge_map):
    hot_to_label = {v: k for k, v in edge_map.items()}
    print(hot_to_label)
    hots = g_dgl.edata['one_hot'].detach().numpy()
    G = dgl.to_networkx(g_dgl)
    labels = {e: hot_to_label[i] for i, e in zip(hots, G.edges())}
    print(labels)
    nx.set_edge_attributes(G, labels, 'label')
    print(G.nodes())
    G = nx.to_undirected(G)
    return G
Beispiel #7
0
    def convert_dgl_to_nx(G: dgl.DGLGraph) -> nx.Graph:
        """
        Converts a DGL Graph (``dgl.DGLGraph``) to a NetworkX (``nx.Graph``) object. Preserves node and edge attributes.

        :param G: ``dgl.DGLGraph`` to convert to ``NetworkX`` graph.
        :type G: dgl.DGLGraph
        :return: NetworkX graph object.
        :rtype: nx.Graph
        """
        node_attrs = G.node_attr_schemes().keys()
        edge_attrs = G.edge_attr_schemes().keys()
        return dgl.to_networkx(G, node_attrs, edge_attrs)
Beispiel #8
0
 def state(self):
     features = torch.stack([
         x for i, x in enumerate(self.dataset.ndata['feat'])
         if i in self.picked_nodes
     ])
     features = np.array(features)
     graph = dgl.add_self_loop(self.graph)
     graph = dgl.to_networkx(graph).to_undirected()
     self.embedding_model.fit(graph, features)
     embedding = self.embedding_model.get_embedding()
     embedding = np.sum(embedding, axis=0)
     return embedding
Beispiel #9
0
    def convert_dgl_to_nx(G: dgl.DGLGraph) -> nx.Graph:
        """
        Converts a DGL Graph (dgl.DGLGraph) to a NetworkX (nx.Graph) object. Preservers node and edge attributes.

        :param G: dgl.DGLGraph to convert to NetworkX
        :type G: dgl.DGLGraph
        :return: NetworkX graph object
        :rtype: nx.Graph
        """
        node_attrs = G.node_attr_schemes().keys()
        edge_attrs = G.edge_attr_schemes().keys()
        nx_g = dgl.to_networkx(G, node_attrs, edge_attrs)
        return nx_g
Beispiel #10
0
def process_dataset(name, epsilon):
    if name == 'cora':
        dataset = CoraGraphDataset()
    elif name == 'citeseer':
        dataset = CiteseerGraphDataset()

    graph = dataset[0]
    feat = graph.ndata.pop('feat')
    label = graph.ndata.pop('label')

    train_mask = graph.ndata.pop('train_mask')
    val_mask = graph.ndata.pop('val_mask')
    test_mask = graph.ndata.pop('test_mask')

    train_idx = th.nonzero(train_mask, as_tuple=False).squeeze()
    val_idx = th.nonzero(val_mask, as_tuple=False).squeeze()
    test_idx = th.nonzero(test_mask, as_tuple=False).squeeze()

    nx_g = dgl.to_networkx(graph)

    print('computing ppr')
    diff_adj = compute_ppr(nx_g, 0.2)
    print('computing end')

    if name == 'citeseer':
        print('additional processing')
        feat = th.tensor(preprocess_features(feat.numpy())).float()
        diff_adj[diff_adj < epsilon] = 0
        scaler = MinMaxScaler()
        scaler.fit(diff_adj)
        diff_adj = scaler.transform(diff_adj)

    diff_edges = np.nonzero(diff_adj)
    diff_weight = diff_adj[diff_edges]
    diff_graph = dgl.graph(diff_edges)

    graph = graph.add_self_loop()

    return graph, diff_graph, feat, label, train_idx, val_idx, test_idx, diff_weight
Beispiel #11
0
    def nx_graph_from_pdb_file(self,
                               pdb_code,
                               chain_selection='all',
                               contact_file=None):
        """
        Produces a NetworkX Graph Object

        :param pdb_code: 4 character PDB accession code
        :type pdb_code: str
        :param chain_selection: string indicating chain selection {'A', 'B', 'AB', ..., 'all'}
        :type chain_selection: str
        :param contact_file: Path to GetContacts output file.
        :type contact_file: str, optional
        :return: NetworkX graph object of protein
        """
        g, resiude_name_encoder, residue_id_encoder = self.dgl_graph_from_pdb_file(
            pdb_code, chain_selection, contact_file)
        node_attrs = g.node_attr_schemes().keys()
        edge_attrs = g.edge_attr_schemes().keys()
        return dgl.to_networkx(
            g, node_attrs,
            edge_attrs), resiude_name_encoder, residue_id_encoder
Beispiel #12
0
    def nx_graph_from_pdb_code(self,
                               pdb_code,
                               chain_selection='all',
                               contact_file=None,
                               edge_construction=['contacts'],
                               encoding=False,
                               k_nn=None,
                               custom_edges=None):
        """
        Produces a NetworkX Graph Object

        :param encoding:
        :type bool:
        :param edges: User-supplied edges, defaults to None
        :type edges: Pandas DataFrame, optional
        :param pdb_code: 4 character PDB accession code
        :type pdb_code: str
        :param chain_selection: string indicating chain selection {'A', 'B', 'AB', ..., 'all'}, defaults to 'all'
        :type chain_selection: str
        :param contact_file: Path to GetContacts output file.
        :type contact_file: str, optional
        :return: NetworkX graph object of protein
        :rtype: NetworkX graph
        """
        assert encoding, 'Non-numeric feature encoding must be True'
        g, resiude_name_encoder, residue_id_encoder = self.dgl_graph_from_pdb_code(
            pdb_code=pdb_code,
            chain_selection=chain_selection,
            contact_file=contact_file,
            edge_construction=edge_construction,
            custom_edges=custom_edges,
            encoding=encoding,
            k_nn=k_nn)
        node_attrs = g.node_attr_schemes().keys()
        edge_attrs = g.edge_attr_schemes().keys()

        return dgl.to_networkx(
            g, node_attrs,
            edge_attrs), resiude_name_encoder, residue_id_encoder
Beispiel #13
0
def atest_nx_conversion(index_dtype):
    # check conversion between networkx and DGLGraph

    def _check_nx_feature(nxg, nf, ef):
        # check node and edge feature of nxg
        # this is used to check to_networkx
        num_nodes = len(nxg)
        num_edges = nxg.size()
        if num_nodes > 0:
            node_feat = ddict(list)
            for nid, attr in nxg.nodes(data=True):
                assert len(attr) == len(nf)
                for k in nxg.nodes[nid]:
                    node_feat[k].append(F.unsqueeze(attr[k], 0))
            for k in node_feat:
                feat = F.cat(node_feat[k], 0)
                assert F.allclose(feat, nf[k])
        else:
            assert len(nf) == 0
        if num_edges > 0:
            edge_feat = ddict(lambda: [0] * num_edges)
            for u, v, attr in nxg.edges(data=True):
                assert len(attr) == len(ef) + 1  # extra id
                eid = attr['id']
                for k in ef:
                    edge_feat[k][eid] = F.unsqueeze(attr[k], 0)
            for k in edge_feat:
                feat = F.cat(edge_feat[k], 0)
                assert F.allclose(feat, ef[k])
        else:
            assert len(ef) == 0

    n1 = F.randn((5, 3))
    n2 = F.randn((5, 10))
    n3 = F.randn((5, 4))
    e1 = F.randn((4, 5))
    e2 = F.randn((4, 7))
    g = dgl.graph([(0, 2), (1, 4), (3, 0), (4, 3)], index_dtype=index_dtype)
    g.ndata.update({'n1': n1, 'n2': n2, 'n3': n3})
    g.edata.update({'e1': e1, 'e2': e2})

    # convert to networkx
    nxg = dgl.to_networkx(g, node_attrs=['n1', 'n3'], edge_attrs=['e1', 'e2'])
    assert len(nxg) == 5
    assert nxg.size() == 4
    _check_nx_feature(nxg, {'n1': n1, 'n3': n3}, {'e1': e1, 'e2': e2})

    # convert to DGLGraph, nx graph has id in edge feature
    # use id feature to test non-tensor copy
    g = dgl.graph(nxg,
                  node_attrs=['n1'],
                  edge_attrs=['e1', 'id'],
                  index_dtype=index_dtype)
    assert g._idtype_str == index_dtype
    # check graph size
    assert g.number_of_nodes() == 5
    assert g.number_of_edges() == 4
    # check number of features
    # test with existing dglgraph (so existing features should be cleared)
    assert len(g.ndata) == 1
    assert len(g.edata) == 2
    # check feature values
    assert F.allclose(g.ndata['n1'], n1)
    # with id in nx edge feature, e1 should follow original order
    assert F.allclose(g.edata['e1'], e1)
    assert F.array_equal(g.edata['id'], F.copy_to(F.arange(0, 4), F.cpu()))

    # test conversion after modifying DGLGraph
    # TODO(minjie): enable after mutation is supported
    #g.pop_e_repr('id') # pop id so we don't need to provide id when adding edges
    #new_n = F.randn((2, 3))
    #new_e = F.randn((3, 5))
    #g.add_nodes(2, data={'n1': new_n})
    ## add three edges, one is a multi-edge
    #g.add_edges([3, 6, 0], [4, 5, 2], data={'e1': new_e})
    #n1 = F.cat((n1, new_n), 0)
    #e1 = F.cat((e1, new_e), 0)
    ## convert to networkx again
    #nxg = g.to_networkx(node_attrs=['n1'], edge_attrs=['e1'])
    #assert len(nxg) == 7
    #assert nxg.size() == 7
    #_check_nx_feature(nxg, {'n1': n1}, {'e1': e1})

    # now test convert from networkx without id in edge feature
    # first pop id in edge feature
    for _, _, attr in nxg.edges(data=True):
        attr.pop('id')
    # test with a new graph
    g = dgl.graph(nxg, node_attrs=['n1'], edge_attrs=['e1'])
    # check graph size
    assert g.number_of_nodes() == 5
    assert g.number_of_edges() == 4
    # check number of features
    assert len(g.ndata) == 1
    assert len(g.edata) == 1
    # check feature values
    assert F.allclose(g.ndata['n1'], n1)
    # edge feature order follows nxg.edges()
    edge_feat = []
    for _, _, attr in nxg.edges(data=True):
        edge_feat.append(F.unsqueeze(attr['e1'], 0))
    edge_feat = F.cat(edge_feat, 0)
    assert F.allclose(g.edata['e1'], edge_feat)

    # Test converting from a networkx graph whose nodes are
    # not labeled with consecutive-integers.
    nxg = nx.cycle_graph(5)
    nxg.remove_nodes_from([0, 4])
    for u in nxg.nodes():
        nxg.nodes[u]['h'] = F.tensor([u])
    for u, v, d in nxg.edges(data=True):
        d['h'] = F.tensor([u, v])

    g = dgl.DGLGraph()
    g.from_networkx(nxg, node_attrs=['h'], edge_attrs=['h'])
    assert g.number_of_nodes() == 3
    assert g.number_of_edges() == 4
    assert g.has_edge_between(0, 1)
    assert g.has_edge_between(1, 2)
    assert F.allclose(g.ndata['h'], F.tensor([[1.], [2.], [3.]]))
    assert F.allclose(g.edata['h'],
                      F.tensor([[1., 2.], [1., 2.], [2., 3.], [2., 3.]]))
Beispiel #14
0
                    action='store_true',
                    default=True,
                    help="indicates whether to use early stop or not")
parser.add_argument('--fastmode',
                    action="store_true",
                    default=False,
                    help="skip re-evaluate the validation set")
args = parser.parse_args()

data = CoraGraphDataset()
dataset = data[0]
dataset = dgl.add_self_loop(dataset)

embedding_model = TENE()
embedding_model.fit(
    dgl.to_networkx(dataset).to_undirected(), np.array(dataset.ndata['feat']))
dataset_embedding = embedding_model.get_embedding()
dataset_embedding = np.sum(dataset_embedding, axis=0)

heads = ([args.num_heads] * args.num_layers) + [args.num_out_heads]
GNN_model = GAT(args.num_layers, dataset.ndata['feat'].shape[1],
                args.num_hidden, data.num_labels, heads, F.elu, args.in_drop,
                args.attn_drop, args.negative_slope, args.residual)
GNN_model.load_state_dict(torch.load('GAT/es_checkpoint.pt'))

env = myenv(GNN_model, embedding_model, dataset)
env.reset()

# if gpu is to be used
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
Beispiel #15
0
def plot_graph(graph, path):
    nx_graph = dgl.to_networkx(graph)
    pos = nx.kamada_kawai_layout(nx_graph)
    nx.draw(nx_graph, pos, with_labels=True, node_color=[[.7, .7, .7]])
    plt.savefig(path)
    plt.close()
import numpy as np
import networkx as nx
from karateclub.node_embedding.attributed import TENE

import dgl
from dgl.data import CoraGraphDataset

data = CoraGraphDataset()
g = data[0]
g = dgl.add_self_loop(
    dgl.node_subgraph(g, list(set(np.random.choice(len(g.nodes()), 5)))))
X = np.array(g.ndata['feat'])
g = dgl.to_networkx(g).to_undirected()

# g = nx.newman_watts_strogatz_graph(200, 20, 0.05)
#
# X = np.random.uniform(0, 1, (200, 200))

model = TENE()

model.fit(g, X)
embedding = model.get_embedding()
embedding = np.sum(embedding, axis=0)
# print(np.concatenate((embedding, embedding), axis=0).shape)
print(embedding.shape)
Beispiel #17
0
def visualize_sub_graph(sub_graph,
                        edge_weights=None,
                        origin_nodes=None,
                        center_node=None):
    """
    Use networkx to visualize the sub_graph and,
    if edge weights are given, set edges with different fading of blue.

    Parameters
    ----------
    sub_graph: DGLGraph, the sub_graph to be visualized.
    edge_weights: Tensor, the same number of edges. Values are (0,1), default is None
    origin_nodes: List, list of node ids that will be used to replace the node ids in the subgraph in visualization
    center_node: Tensor, the node id in origin node list to be highlighted with different color

    Returns
    show the sub_graph
    -------

    """
    # Extract original idx and map to the new networkx graph
    # Convert to networkx graph
    g = dgl.to_networkx(sub_graph)
    nx_edges = g.edges(data=True)

    if not (origin_nodes is None):
        n_mapping = {
            new_id: old_id
            for new_id, old_id in enumerate(origin_nodes.tolist())
        }
        g = nx.relabel_nodes(g, mapping=n_mapping)

    pos = nx.spring_layout(g)

    if edge_weights is None:
        options = {
            "node_size": 1000,
            "alpha": 0.9,
            "font_size": 24,
            "width": 4,
        }
    else:

        ec = [edge_weights[e[2]['id']][0] for e in nx_edges]
        options = {
            "node_size": 1000,
            "alpha": 0.3,
            "font_size": 12,
            "edge_color": ec,
            "width": 4,
            "edge_cmap": plt.cm.Reds,
            "edge_vmin": 0,
            "edge_vmax": 1,
            "connectionstyle": "arc3,rad=0.1"
        }

    nx.draw(g, pos, with_labels=True, node_color='b', **options)
    if not (center_node is None):
        nx.draw(g,
                pos,
                nodelist=center_node.tolist(),
                with_labels=True,
                node_color='r',
                **options)

    plt.show()
Beispiel #18
0
import dgl
from dgl.data import CoraGraphDataset, CiteseerGraphDataset, PubmedGraphDataset
import networkx as nx
import torch
import torch.nn.functional as F
import numpy as np
from GAT.model import GAT

data = CoraGraphDataset()
dataset = data[0]
picked_nodes = list(set(np.random.choice(dataset.nodes().numpy(), 200)))
graph = dgl.node_subgraph(dataset, picked_nodes)
graph = dgl.add_self_loop(graph)
features = torch.stack(
    [x for i, x in enumerate(dataset.ndata['feat']) if i in picked_nodes])
g = dgl.to_networkx(graph)
f = np.array(dataset.ndata['feat'])
print("*************")
print(graph.edata['_ID'])

parser = argparse.ArgumentParser(description='GAT')
parser.add_argument("--gpu",
                    type=int,
                    default=0,
                    help="which GPU to use. Set -1 to use CPU.")
parser.add_argument("--epochs",
                    type=int,
                    default=500,
                    help="number of training epochs")
parser.add_argument("--num-heads",
                    type=int,
Beispiel #19
0
def visualize_wind_farm(g: dgl.DGLGraph,
                        min_distance: float,
                        angle_threshold: float,
                        wind_direction: float,
                        wind_speed: float,
                        x_grid_size: float,
                        y_grid_size: float,
                        dir_mark_margin=0.01,
                        dir_mark_len=0.03,
                        arrow_size=0.01,
                        viz_eps=0.02,
                        dpi=250,
                        label_size=15,
                        tick_size=12,
                        annotation_size=12,
                        show_color_bar_label=False,
                        edge_width=2.0,
                        legend_size=15):
    # Figure drawing parameters
    influential_region_zorder = -1
    min_distance_region_zorder = 0
    edge_zorder = 2
    turbine_zorder = 3

    scatter_line_width = 2.0
    scatter_ball_size = 200
    x_limit = x_grid_size
    y_limit = y_grid_size

    fig, (ax,
          ax_colorbar) = plt.subplots(1,
                                      2,
                                      figsize=(10.5, 10),
                                      gridspec_kw={'width_ratios': [10, 0.5]},
                                      dpi=dpi)
    fig.tight_layout(rect=[0, 0.03, 1, 0.95])

    ax.xaxis.grid(True, which='major')
    ax.xaxis.grid(True, which='minor')
    ax.yaxis.grid(True, which='major')
    ax.yaxis.grid(True, which='minor')
    ax.set_xlim([-x_limit * viz_eps, x_limit * (1 + viz_eps)])
    ax.set_ylim([-y_limit * viz_eps, y_limit * (1 + viz_eps)])
    ax.tick_params(labelsize=tick_size)

    # Draw turbines color-coded with powers
    x, y = g.ndata['x'], g.ndata['y']
    cmap = matplotlib.cm.Blues
    norm = matplotlib.colors.Normalize(vmin=0.0, vmax=1.0)
    turbine_scatter = ax.scatter(x,
                                 y,
                                 label='Turbine',
                                 cmap=cmap,
                                 c=norm(g.ndata['power']),
                                 edgecolors='#bbbbbb',
                                 linewidths=scatter_line_width,
                                 s=scatter_ball_size,
                                 zorder=turbine_zorder)

    # Draw interaction edges
    xys = torch.cat([x, y], dim=-1).squeeze().numpy()
    pos_dict = {}
    for i, xy in enumerate(xys):
        pos_dict[i] = xy
    nxg = dgl.to_networkx(g)
    nx.draw_networkx_edges(nxg,
                           edge_color='grey',
                           ax=ax,
                           width=edge_width,
                           pos=pos_dict)

    ax.set_xlabel("Wind farm X size (m)", fontsize=label_size)
    ax.set_ylabel("Wind farm Y size (m)", fontsize=label_size)

    cbar = fig.colorbar(turbine_scatter,
                        cax=ax_colorbar,
                        ticks=np.arange(0.0, 1.1, 0.05))

    if show_color_bar_label:
        cbar.set_label('Power', fontsize=label_size)
    cbar.set_clim(0.0, 1.0)
    cbar.ax.tick_params(labelsize=tick_size)

    for i, (x, y) in enumerate(zip(g.ndata['x'], g.ndata['y'])):
        # Add min-distance circle
        circle = plt.Circle((x, y),
                            min_distance / 2,
                            hatch='....',
                            facecolor='white',
                            edgecolor='#ffa45c',
                            alpha=1.0,
                            zorder=min_distance_region_zorder)

        # Add annotation for depicting turbine index and the corresponding power
        ax.annotate("T{}".format(i + 1),
                    xy=(x, y + y_limit * 0.025),
                    fontsize=annotation_size,
                    horizontalalignment="center")
        # ax.annotate("Power : {:.2f}".format(index_power_dict[node.index]),
        #             (x, y - y_limit * viz_eps))
        ax.add_artist(circle)

        # Add influential cones
        wedge = Wedge(
            (x, y),
            np.sqrt(x_grid_size**2 + y_grid_size**2),  # radius
            270 - wind_direction - angle_threshold,
            # from theta 1 (in degrees) # FLORIS 1.4 measures 270 degree as left
            270 - wind_direction + angle_threshold,  # to theta 2 (in degrees)
            color='g',
            alpha=0.05,
            zorder=influential_region_zorder)
        ax.add_patch(wedge)

    # Draw directional mark
    dir_mark_center_x = x_limit * (1 - 2 * dir_mark_margin - dir_mark_len)
    dir_mark_center_y = y_limit * (1 - 2 * dir_mark_margin - dir_mark_len)

    marker_len = x_limit * dir_mark_len

    # WEST
    dir_mark_west_x = dir_mark_center_x - marker_len
    dir_mark_west_y = dir_mark_center_y

    # EAST
    dir_mark_east_x = dir_mark_center_x + marker_len
    dir_mark_east_y = dir_mark_center_y

    # NORTH
    dir_mark_north_x = dir_mark_center_x
    dir_mark_north_y = dir_mark_center_y + marker_len

    # SOUTH
    dir_mark_south_x = dir_mark_center_x
    dir_mark_south_y = dir_mark_center_y - marker_len

    # Visualize wind direction

    # Compass
    compass_color = 'k'
    wd_color = 'orange'
    arrow_size = x_limit * arrow_size

    # WEST -> EAST
    w2e = Line2D((dir_mark_west_x, dir_mark_east_x),
                 (dir_mark_west_y, dir_mark_east_y),
                 alpha=0.5,
                 c=compass_color)

    ax.add_line(w2e)

    # NORTH -> SOUTH
    n2s = Line2D((dir_mark_north_x, dir_mark_south_x),
                 (dir_mark_north_y, dir_mark_south_y),
                 alpha=0.5,
                 c=compass_color)

    ax.add_line(n2s)

    # NORTH -> WEST
    n2w = Line2D((dir_mark_north_x, dir_mark_west_x),
                 (dir_mark_north_y, dir_mark_west_y),
                 alpha=0.5,
                 c=compass_color)

    ax.add_line(n2w)

    # draw wind direction arrow
    wind_dir_rad = np.radians(wind_direction - 270)
    sin = np.sin(wind_dir_rad)
    cos = np.cos(wind_dir_rad)
    wind_start_x = dir_mark_center_x - marker_len * cos  # tail
    wind_start_y = dir_mark_center_y + marker_len * sin

    wind_end_x = dir_mark_center_x + marker_len * cos  # arrow
    wind_end_y = dir_mark_center_y - marker_len * sin

    ax.arrow(wind_start_x,
             wind_start_y,
             wind_end_x - wind_start_x,
             wind_end_y - wind_start_y,
             linewidth=2,
             head_width=arrow_size,
             head_length=arrow_size,
             fc=wd_color,
             ec=wd_color,
             length_includes_head=True,
             zorder=edge_zorder)

    ax.annotate("Wind direction : {}$^\circ$".format(wind_direction),
                (dir_mark_west_x - marker_len * 7.0,
                 dir_mark_center_y + marker_len * 0.5))

    ax.annotate("Wind speed : {} m/s".format(wind_speed),
                (dir_mark_west_x - marker_len * 7.0,
                 dir_mark_center_y - marker_len * 0.5))

    handles, labels = ax.get_legend_handles_labels()
    handles += [wedge, circle]
    labels += ["influential region", "min. distance"]
    ax.legend(handles, labels, prop={'size': legend_size})