Improving Python NetworkX graph layout - python
I am having some problems in visualizing the graphs created with python-networkx, I want to able to reduce clutter and regulate the distance between the nodes (I have also tried spring_layout, it just lays out the nodes in an elliptical fashion). Please advise.
Parts of code:
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)
In networkx, it's worth checking out the graph drawing algorithms provided by graphviz via nx.graphviz_layout.
I've had good success with neato but the other possible inputs are
dot - "hierarchical" or layered drawings of directed graphs. This is the default tool to use if edges have directionality.
neato - "spring model'' layouts. This is the default tool to use if the graph is not too large (about 100 nodes) and you don't know anything else about it. Neato attempts to minimize a global energy function, which is equivalent to statistical multi-dimensional scaling.
fdp - "spring model'' layouts similar to those of neato, but does this by reducing forces rather than working with energy.
sfdp - multiscale version of fdp for the layout of large graphs.
twopi - radial layouts, after Graham Wills 97. Nodes are placed on concentric circles depending their distance from a given root node.
circo - circular layout, after Six and Tollis 99, Kauffman and Wiese 02. This is suitable for certain diagrams of multiple cyclic structures, such as certain telecommunications networks.
In general, graph drawing is a hard problem. If these algorithms are not sufficient, you'll have to write your own or have networkx draw parts individually.
I found this to be useful for quickly visualizing interaction data (here, genes) sourced as a CSV file.
Data file [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.7 venv
import networkx as nx
import matplotlib.pyplot as plt
G = nx.read_edgelist("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
UPDATE
This used to work (2018-03), but now (2019-12) gives a pygraphviz import error:
from networkx.drawing.nx_agraph import graphviz_layout
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)
Traceback (most recent call last):
...
ImportError: libpython3.7m.so.1.0: cannot open shared object file:
No such file or directory
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
...
ImportError: ('requires pygraphviz ', 'http://pygraphviz.github.io/')
SOLUTION
Outside Python (at the venv terminal prompt: $) install pydot.
pip install pydot
Back in Python run the following code.
import warnings
warnings.filterwarnings("ignore", category=UserWarning)
import networkx as nx
import matplotlib.pyplot as plt
G = nx.read_edgelist("a.csv", delimiter=",")
# For a DiGraph() [directed edges; not shown]:
# G = nx.read_edgelist("a.csv", delimiter=",", create_using=nx.DiGraph)
nx.draw(G, pos = nx.nx_pydot.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 attached
The main change was to replace
nx.draw(G, pos = graphviz_layout(G), ...)
with
nx.draw(G, pos = nx.nx_pydot.graphviz_layout(G), ...)
References
Remove matplotlib depreciation warning from showing
What could cause NetworkX & PyGraphViz to work fine alone but not together?
Specifically: https://stackoverflow.com/a/40750101/1904943
Improved plot layout
It is difficult to decrease congestion in these static networkx / matplotlib plots; one workaround is to increase the figure size, per this StackOverflow Q/A: High Resolution Image of a Graph using NetworkX and Matplotlib :
plt.figure(figsize=(20,14))
# <matplotlib.figure.Figure object at 0x7f1b65ea5e80>
nx.draw(G, pos = nx.nx_pydot.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
To reset the output figure size to the system default:
plt.figure()
# <matplotlib.figure.Figure object at 0x7f1b454f1588>
Bonus: shortest path
nx.dijkstra_path(G, 'CDKN1A', 'MLH3')
# ['CDKN1A', 'CDK2', 'CHEK2', 'BRCA1', 'PTEN', 'MLH3']
plot1.png
plot2.png
Although I did not do this here, if you want to add node borders and thicken the node border lines (node edge thickness: linewidths), do the following.
nx.draw(G, pos = nx.nx_pydot.graphviz_layout(G), \
node_size=1200, node_color='lightblue', linewidths=2.0, \
font_size=10, font_weight='bold', with_labels=True)
# Get current axis:
ax = plt.gca()
ax.collections[0].set_edgecolor('r')
# r : red (can also use #FF0000) | b : black (can also use #000000) | ...
plt.show()
You have a lot of data in your graph, so it is going to be hard to remove clutter.
I suggest you to use any standard layout. You said that you used spring_layout. I suggest you to try it again but this time using the weight attribute when adding the edges.
For example:
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()
Additionally you can use the parameter scale to increase the global distance between the nodes.
To answer your question how to regulate the distance between nodes, I expand on Hooked's answer:
If you draw the graph via the Graphviz backend and when you then use the fdp algorithm, you can adjust the distance between nodes by the edge attribute len.
Here a code example, how to draw a graph G and save in the Graphviz file gvfile with wider distance between nodes (default distance for fdp is 0.3):
A = nx.to_agraph(G)
A.edge_attr.update(len=3)
A.write(gv_file_name)
Two comments:
It is normally advisable to adjust len with the number of nodes in the graph.
The len attribute is only recognised by the fdp and neato algorithm, but not e.g. by the sfdp algorithm.
So, this may not be useful for original question, but maybe for future searchers :
A solution can be
pos = nx.nx_agraph.graphviz_layout(G)
nx.draw(G, pos=pos)
Think of installing agraph first with pip install agraph.
(The solution with nx.nx_pydot.graphviz_layout(G) is now to be depreciated according to networkx.)
Hope it helps :)
Related
Python Netgraph Interactive Labels
I am trying to create an interactive plot using netgraph and networkx. I would like the plot to allow movement of the nodes and as the nodes move, the edges and edge_labels will also dynamically update. Moving the nodes was addressed by the author of netgraph here . Now when I make a simpler plot and try to label an edge, the label stays static, and is sometimes not even on an edge. It seems like handling edge_positions similar to node_positions on the last two lines should at least address the label not moving. Why the label isn't anchored to a particular edge is still puzzling. Does anyone know if the desired effect is achievable? Here is a snip before moving anything: Here is a snip after moving the bottom-right node to the top left: Here is my current code: import matplotlib.pyplot as plt import networkx as nx import netgraph # pip install netgraph #Graph creation: G=nx.Graph(type="") for i in range(6): G.add_node(i,shape="o") #Changing shape for two nodes G.nodes[1]['shape'] = "v" G.nodes[5]['shape'] = "v" #Add edges G.add_edge(1,2) G.add_edge(4,5) G.add_edge(0,4) G.add_edge(2,3) G.add_edge(2,4) labs={(1,2):"1 to 2"} nx.draw_networkx_edge_labels(G, pos=nx.spring_layout(G),edge_labels=labs) #Get node shapes node_shapes = nx.get_node_attributes(G,"shape") # Create an interactive plot. # NOTE: you must retain a reference to the object instance! # Otherwise the whole thing will be garbage collected after the initial draw # and you won't be able to move the plot elements around. pos = nx.layout.spring_layout(G) ######## drag nodes around ######### # To access the new node positions: plot_instance = netgraph.InteractiveGraph(G, node_shape=node_shapes, node_positions=pos, edge_positions=pos) node_positions = plot_instance.node_positions edge_positions = plot_instance.edge_positions
To have it as formal answer, you need to add to the InteractiveGraph object the information that you want to draw (and move) the edge labels, i.e. the following netgraph.InteractiveGraph(G, node_shape=node_shapes, node_positions=pos, edge_positions=pos, edge_labels=labs) (With emphasis on the last parameter) As you already, noticed then you don't need the call of nx.draw_networkx_edge_labels.
Highlighting certain nodes/edges in NetworkX - Issues with using zip()
I able to populate network graph using networkx. My problem is when I want to highlight the path (shortest path for example) the graph cannot generate and it will return error below. nx.draw_networkx_edges(Gr,pos,edgelist=path_edges,edge_color='r',width=10) File "/usr/local/lib/python3.6/site-packages/networkx/drawing/nx_pylab.py", line 578, in draw_networkx_edges if not edgelist or len(edgelist) == 0: # no edges! TypeError: object of type 'zip' has no len() I look for solution and this is because the script is running over python3 and due to that I get this error. One of the solution is to change and add list as below. original: Gr = nx.DiGraph() edges = graph Gr.add_edges_from(edges) pos = nx.spring_layout(Gr) path = nx.shortest_path(Gr,source=1,target=7) path_edges = zip(path,path[1:]) nx.draw_networkx_nodes(Gr,pos,nodelist=path,node_color='r') nx.draw_networkx_edges(Gr,pos,edgelist=path_edges,edge_color='r',width=10) plt.axis('equal') plt.show() Modified: path = nx.shortest_path(Gr,source=1,target=7) path_edges = list(zip(path,path[1:])) nx.draw_networkx_nodes(Gr,pos,nodelist=path,node_color='r') nx.draw_networkx_edges(Gr,pos,edgelist=path_edges,edge_color='r',width=10) plt.axis('equal') plt.show() Able to run the script no error and able to generate graph but the highlighted path (red) is not aligned to the topology node and link. The red path should be on-top and align of node/path 1-2-3-4-5-6-7. Please refer to images below Please advise how to resolve this problem.
I was able to generate the graph you appear to be after with the following code - let me know if you encounter any issues. You were correct that you need to convert the zip object to a list, but I think there may be other mistakes in your drawing code. If you need the output from nx.spring_layout to be the same every time, you can use the seed keyword argument, for example pos = nx.spring_layout(Gr, seed=123). Code: import networkx as nx # Set up graph Gr = nx.DiGraph() edges = [(i+1, i+2) for i in range(10)] + [(i+2, i+1) for i in range(10)] Gr.add_edges_from(edges) # Get position using spring layout pos = nx.spring_layout(Gr) # Get shortest path path = nx.shortest_path(Gr,source=1,target=7) path_edges = list(zip(path,path[1:])) # Draw nodes and edges not included in path nx.draw_networkx_nodes(Gr, pos, nodelist=set(Gr.nodes)-set(path)) nx.draw_networkx_edges(Gr, pos, edgelist=set(Gr.edges)-set(path_edges), connectionstyle='arc3, rad = 0.3') # Draw nodes and edges included in path nx.draw_networkx_nodes(Gr, pos, nodelist=path, node_color='r') nx.draw_networkx_edges(Gr,pos,edgelist=path_edges,edge_color='r', connectionstyle='arc3, rad = 0.3') # Draw labels nx.draw_networkx_labels(Gr,pos) Output:
Is it possible to control the order that nodes are drawn using NetworkX in python?
I have a large graph object with many nodes that I am trying to graph. Due to the large number of nodes, many are being drawn one over another. This in itself is not a problem. However, a small percentage of nodes have node attributes which dictate their colour. Ideally I would be able to draw the graph in such a way that nodes with this property are drawn last, on top of the other nodes, so that it is possible to see their distribution across the graph. The code I have so far used to generate the graph is shown below: import networkx as nx import matplotlib.pyplot as plt import numpy as np import os import pickle from pathlib import Path def openFileAtPath(filePath): print('Opening file at: ' + filePath) with open(filePath, 'rb') as input: file = pickle.load(input) return file # Pre manipulation path g = openFileAtPath('../initialGraphs/wordNetadj_dictionary1.11.pkl') # Post manipulation path # g = openFileAtPath('../manipulatedGraphs/wordNetadj_dictionary1.11.pkl') print('Fetching SO scores') scores = list() for node in g.nodes: scores.append(g.node[node]['weight']) print('Drawing network') nx.draw(g, with_labels=False, cmap=plt.get_cmap('RdBu'), node_color=scores, node_size=40, font_size=8) plt.show() And currently the output is as shown: This graph object itself has taken a relatively long time to generate and is computationally intensive, so ideally I wouldn't have to remake the graph from scratch. However, I am fairly sure that the graph is drawn in the same order that the nodes were added to the graph object. I have searched for a way of changing the order that the nodes are stored within the graph object, but given directional graphs actually have an order, my searches always end up with answers showing me how to reverse the direction of a graph. So, is there a way to dictate the order in which nodes are drawn, or alternatively, change the order that nodes are stored inside some graph object. Potentially worthy of a second question, but the edges are also blocked out by the large number of nodes. Is there a way to draw the edges above the nodes behind them?
Piggybacking off Paul Brodersen's answer, if you want different nodes to be in the foreground and background, I think you should do the following: For all nodes that belong in the same layer, draw the subgraph corresponding to the nodes, and set the , as follows: pos = {...} # some dictionary of node positions, required for the function below H = G.subgraph(nbunch) collection = nx.draw_networkx_nodes(H, pos) collection.set_zorder(zorder) Do this for every group of nodes that belong in the same level. It's tedious, but it will do the trick. Here is a toy example that I created based on looking up this question as part of my own research import matplotlib as mpl mpl.use('agg') import pylab import networkx as nx G = nx.Graph() G.add_path([1, 2, 3, 4]) pos = {1 : (0, 0), 2 : (0.5, 0), 3 : (1, 0), 4 : (1.5, 0)} for node in G.nodes(): H = G.subgraph([node]) collection = nx.draw_networkx_nodes(H, pos) collection.set_zorder(node) pylab.plot([0, 2], [0, 0], zorder=2.5) pylab.savefig('nodes_zorder.pdf', format='pdf') pylab.close() This makes a graph, and then puts the each node at a successively higher level going from left to right, so the leftmost node is farthest in the background and the rightmost node is farthest in the foreground. It then draws a straight line whose zorder is 2. As a result, it comes in front of the two left nodes, and behind the two right nodes. Here is the result.
draw is a wrapper around draw_networkx_nodes and draw_networkx_edges. Unlike draw, the two functions return their respective artists ( PathCollection and LineCollection, IIRC). These are your standard matplotlib artists, and as as such their relative draw order can be controlled via their zorder attribute.
how to draw communities with networkx
How can I draw a graph with it's communities using python networkx like this image : image url
The documentation for networkx.draw_networkx_nodes and networkx.draw_networkx_edges explains how to set the node and edge colors. The patches bounding the communities can be made by finding the positions of the nodes for each community and then drawing a patch (e.g. matplotlib.patches.Circle) that contains all positions (and then some). The hard bit is the graph layout / setting the node positions. AFAIK, there is no routine in networkx to achieve the desired graph layout "out of the box". What you want to do is the following: Position the communities with respect to each other: create a new, weighted graph, where each node corresponds to a community, and the weights correspond to the number of edges between communities. Get a decent layout with your favourite graph layout algorithm (e.g.spring_layout). Position the nodes within each community: for each community, create a new graph. Find a layout for the subgraph. Combine node positions in 1) and 3). E.g. scale community positions calculated in 1) by a factor of 10; add those values to the positions of all nodes (as computed in 2)) within that community. I have been wanting to implement this for a while. I might do it later today or over the weekend. EDIT: Voila. Now you just need to draw your favourite patch around (behind) the nodes. import numpy as np import matplotlib.pyplot as plt import networkx as nx def community_layout(g, partition): """ Compute the layout for a modular graph. Arguments: ---------- g -- networkx.Graph or networkx.DiGraph instance graph to plot partition -- dict mapping int node -> int community graph partitions Returns: -------- pos -- dict mapping int node -> (float x, float y) node positions """ pos_communities = _position_communities(g, partition, scale=3.) pos_nodes = _position_nodes(g, partition, scale=1.) # combine positions pos = dict() for node in g.nodes(): pos[node] = pos_communities[node] + pos_nodes[node] return pos def _position_communities(g, partition, **kwargs): # create a weighted graph, in which each node corresponds to a community, # and each edge weight to the number of edges between communities between_community_edges = _find_between_community_edges(g, partition) communities = set(partition.values()) hypergraph = nx.DiGraph() hypergraph.add_nodes_from(communities) for (ci, cj), edges in between_community_edges.items(): hypergraph.add_edge(ci, cj, weight=len(edges)) # find layout for communities pos_communities = nx.spring_layout(hypergraph, **kwargs) # set node positions to position of community pos = dict() for node, community in partition.items(): pos[node] = pos_communities[community] return pos def _find_between_community_edges(g, partition): edges = dict() for (ni, nj) in g.edges(): ci = partition[ni] cj = partition[nj] if ci != cj: try: edges[(ci, cj)] += [(ni, nj)] except KeyError: edges[(ci, cj)] = [(ni, nj)] return edges def _position_nodes(g, partition, **kwargs): """ Positions nodes within communities. """ communities = dict() for node, community in partition.items(): try: communities[community] += [node] except KeyError: communities[community] = [node] pos = dict() for ci, nodes in communities.items(): subgraph = g.subgraph(nodes) pos_subgraph = nx.spring_layout(subgraph, **kwargs) pos.update(pos_subgraph) return pos def test(): # to install networkx 2.0 compatible version of python-louvain use: # pip install -U git+https://github.com/taynaud/python-louvain.git#networkx2 from community import community_louvain g = nx.karate_club_graph() partition = community_louvain.best_partition(g) pos = community_layout(g, partition) nx.draw(g, pos, node_color=list(partition.values())); plt.show() return Addendum Although the general idea is sound, my old implementation above has a few issues. Most importantly, the implementation doesn't work very well for unevenly sized communities. Specifically, _position_communities gives each community the same amount of real estate on the canvas. If some of the communities are much larger than others, these communities end up being compressed into the same amount of space as the small communities. Obviously, this does not reflect the structure of the graph very well. I have written a library for visualizing networks, which is called netgraph. It includes an improved version of the community layout routine outlined above, which also considers the sizes of the communities when arranging them. It is fully compatible with networkx and igraph Graph objects, so it should be easy and fast to make great looking graphs (at least that is the idea). import matplotlib.pyplot as plt import networkx as nx # installation easiest via pip: # pip install netgraph from netgraph import Graph # create a modular graph partition_sizes = [10, 20, 30, 40] g = nx.random_partition_graph(partition_sizes, 0.5, 0.1) # since we created the graph, we know the best partition: node_to_community = dict() node = 0 for community_id, size in enumerate(partition_sizes): for _ in range(size): node_to_community[node] = community_id node += 1 # # alternatively, we can infer the best partition using Louvain: # from community import community_louvain # node_to_community = community_louvain.best_partition(g) community_to_color = { 0 : 'tab:blue', 1 : 'tab:orange', 2 : 'tab:green', 3 : 'tab:red', } node_color = {node: community_to_color[community_id] for node, community_id in node_to_community.items()} Graph(g, node_color=node_color, node_edge_width=0, edge_alpha=0.1, node_layout='community', node_layout_kwargs=dict(node_to_community=node_to_community), edge_layout='bundled', edge_layout_kwargs=dict(k=2000), ) plt.show()
Drawing a custom diagram in python
I wanna draw something like this : The closest thing to this I could find was NetworkX Edge Colormap: http://networkx.github.io/documentation/latest/examples/drawing/edge_colormap.html and here is the source code: #!/usr/bin/env python """ Draw a graph with matplotlib, color edges. You must have matplotlib>=87.7 for this to work. """ __author__ = """Aric Hagberg (hagberg#lanl.gov)""" try: import matplotlib.pyplot as plt except: raise import networkx as nx G=nx.star_graph(20) pos=nx.spring_layout(G) colors=range(20) nx.draw(G,pos,node_color='#A0CBE2',edge_color=colors,width=4,edge_cmap=plt.cm.Blues,with_labels=False) plt.savefig("edge_colormap.png") # save as png plt.show() # display After playing around with their source code, I can't figure out how to hardcode distance of the edge circles from the centre. Right now its random. Also how do I label the edge circles and their distance from the centre? I know for position comes from pos=nx.spring_layout(G). So I looked at the spring_layout attribute and found that position can be specified by using a pos variable which is a dictionary with nodes as keys and values as a list. (https://networkx.github.io/documentation/latest/reference/generated/networkx.drawing.layout.spring_layout.html) But even when I do the following result is random edges : ap = {'uniwide':[55,34,1],'eduram':[34],'uniwide_webauth':[20,55,39],'uniwide_guest':[55,34],'tele9751_lab':[100],'HomeSDN':[100],'TP-LINK':[39]} pos=nx.spring_layout(G,pos=ap)
You can set the node positions explicitly with the pos dictionary. For example import networkx as nx import matplotlib.pyplot as plt G = nx.Graph() G.add_edge('center',1) G.add_edge('center',2) G.add_edge('center',3) G.add_edge('center',4) pos = {'center':(0,0), 1:(1,0), 2:(0,1), 3:(-1,0), 4:(0,-1) } nx.draw(G, pos=pos, with_labels=True) plt.show()
I'm trying to be as helpful as I can. I wouldn't try to keep them static. You'll want to add and remove things, and the algorithm's automatic placement is something you don't want to lose. According to the docs, you should probably tweak k. It looks like n is 20, so multiply k times some factor to increase the distance. n = 20 nx.spring_layout(G, k=(1.0/pow(n, .5))) # what it currently is should maybe be this: nx.spring_layout(G, k=(1.0/pow(n, .5))*1.5) # play around with this factor