Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create Interactive hierarchy diagram from pandas/dictionary

I have data that shows the relationship for each employee with their managers(Person:Manager) -

data = {'PersonX':'Person1', 'PersonY':'Person1', 'PersonZ':'Person 2', 'Person1':'Person100','Person2':'Person100' }

I am trying to show a hierarchy chart from the above data in a clean looking chart and if I can filter that data in the visualization itself that is a Bonus.

The data that I get can contain sometimes 5 people or sometimes the number of records is more than 5000.

I have tried these approaches but they are no where close to generating any graphs that are interactive.

Code -

Try 1 -

import pandas as pd
import networkx as nx

d = {'PersonX': 'Person1', 'PersonY': 'Person1', 'PersonZ': 'Person2', 'Person1': 'Person100', 'Person2': 'Person100'}
df = pd.DataFrame(d.items(), columns=['Person', 'Manager'])
G = nx.from_pandas_edgelist(df,  source='Person', target='Manager')
nx.draw(G, with_labels=True)
plt.show()

Network X graph

Try 2 -

import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder
from scipy.cluster import hierarchy
df2 = df.apply(LabelEncoder().fit_transform)
df2.set_index('Manager', inplace=True)
Z = hierarchy.linkage(df2, 'ward')
hierarchy.dendrogram(hierarchy.linkage(df2, method='ward'))
plt.show()

Attempt 2nd

Try 3 -

print('strict digraph tree {')
for row in d.items():
    print('    {0} -> {1};'.format(*row))
print('}')

And ran the

test.py | dot -Tpng -otree.png

GraphViz

like image 408
Steve_Greenwood Avatar asked Mar 10 '20 20:03

Steve_Greenwood


2 Answers

I went with the following code to create a graph that was interactive, this is a work in progress but I wanted to post this so that people can use this in case needed.

import pandas as pd
import dash
import dash_html_components as html
import dash_cytoscape as cyto
from matplotlib import colors as mcolors
from itertools import zip_longest
from ast import literal_eval

colors = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS)
# Sort colors by hue, saturation, value and name.
by_hsv = sorted((tuple(mcolors.rgb_to_hsv(mcolors.to_rgba(color)[:3])), name)
                for name, color in colors.items())
sorted_names = [name for hsv, name in by_hsv]

app = dash.Dash(__name__)
# colors = ['red', 'blue', 'green', 'yellow', 'pink']

# stylesheet for the web page generated

default_stylesheet = [
    {
        "selector": 'node',
        'style': {
            "opacity": 0.9,
            'height': 15,
            'width': 15,
            'background-color': '#222222',
            'label': 'data(label)'
        }
    },
    {
        "selector": 'edge',
        'style': {
            "curve-style": "bezier",
            "opacity": 0.3,
            'width': 2
        }
    },
    *[{
        "selector": '.' + color,
        'style': {'line-color': color}
    } for color in sorted_names]
]

# Example data for illustration
# My actual data was in the excel file with two columns Managers and Person

managers = ['Person A',
            'Person A',
            'Person A',
            'Person A',
            'Person A',
            'Person A',
            'Person B',
            'Person B',
            'Person B',
            'Person B',
            'Person B',
            'Person B',
            'Person C',
            'Person C',
            'Person C',
            'Person C',
            'Person C',
            'Person C',
            'Person V',
            'Person V',
            'Person V',
            'Person V',
            'Person V']

person = ['Person D',
          'Person E',
          'Person F',
          'Person G',
          'Person H',
          'Person I',
          'Person J',
          'Person K',
          'Person L',
          'Person M',
          'Person N',
          'Person O',
          'Person P',
          'Person Q',
          'Person R',
          'Person S',
          'Person T',
          'Person U',
          'Person A',
          'Person W',
          'Person X',
          'Person B',
          'Person C']

# Creating a dataframe with the illustration data
df = pd.DataFrame(list(zip(person, managers)), columns=['Person', 'Manager'])
# Giving colors to each managers in the dataframe
df['colors'] = df['Manager'].map(dict(zip_longest(list(set(managers)), sorted_names)))
# Creating the nodes within the dataframe
df['y_node_target'] = "{\"data\": {\"id\": \"" + df['Person'] + "\", \"label\":\""+df['Person']+"\"}, \"classes\": \"" + df['colors'] + "\"}"
df['y_node'] = "{\"data\": {\"id\": \"" + df['Manager'] + "\", \"label\":\""+df['Manager']+"\"}, \"classes\": \"" + df['colors'] + "\"}"
nodes = list(set(pd.concat([df['y_node'], df['y_node_target']]).to_list()))
df['Edges'] = "{\'data\': {\'source\':\"" + df['Manager'] + "\", \'target\': \"" + df[
    'Person'] + "\"},\'classes\': \"" + df['colors'] + "\"}"


# Converting the strings to dictionaries and assigning them to variables
edges = list(set(df['Edges'].astype(str).to_list()))
edges = list(map(literal_eval, edges))
nodes = list(map(literal_eval, nodes))

app.layout = html.Div([
    cyto.Cytoscape(
        id='cytoscape',
        elements=edges + nodes,
        stylesheet=default_stylesheet,
        layout={
            'name': 'breadthfirst'
        },
        style={'height': '95vh', 'width': '100%'}
    )
])

if __name__ == '__main__':
    app.run_server(debug=True)

Output was a webpage -

enter image description here

like image 140
Steve_Greenwood Avatar answered Oct 17 '22 03:10

Steve_Greenwood


You can try using Plotly to create an interactive diagram for your graph. Here is an example from their documentation:

Create random graph

import plotly.graph_objects as go

import networkx as nx

G = nx.random_geometric_graph(200, 0.125)

Create edges

edge_x = []
edge_y = []
for edge in G.edges():
    x0, y0 = G.nodes[edge[0]]['pos']
    x1, y1 = G.nodes[edge[1]]['pos']
    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_trace = go.Scatter(
    x=edge_x, y=edge_y,
    line=dict(width=0.5, color='#888'),
    hoverinfo='none',
    mode='lines')

node_x = []
node_y = []
for node in G.nodes():
    x, y = G.nodes[node]['pos']
    node_x.append(x)
    node_y.append(y)

node_trace = go.Scatter(
    x=node_x, y=node_y,
    mode='markers',
    hoverinfo='text',
    marker=dict(
        showscale=True,
        # colorscale options
        #'Greys' | 'YlGnBu' | 'Greens' | 'YlOrRd' | 'Bluered' | 'RdBu' |
        #'Reds' | 'Blues' | 'Picnic' | 'Rainbow' | 'Portland' | 'Jet' |
        #'Hot' | 'Blackbody' | 'Earth' | 'Electric' | 'Viridis' |
        colorscale='YlGnBu',
        reversescale=True,
        color=[],
        size=10,
        colorbar=dict(
            thickness=15,
            title='Node Connections',
            xanchor='left',
            titleside='right'
        ),
        line_width=2))

Color node points

node_adjacencies = []
node_text = []
for node, adjacencies in enumerate(G.adjacency()):
    node_adjacencies.append(len(adjacencies[1]))
    node_text.append('# of connections: '+str(len(adjacencies[1])))

node_trace.marker.color = node_adjacencies
node_trace.text = node_text

Network graph

fig = go.Figure(data=[edge_trace, node_trace],
             layout=go.Layout(
                title='<br>Network graph made with Python',
                titlefont_size=16,
                showlegend=False,
                hovermode='closest',
                margin=dict(b=20,l=5,r=5,t=40),
                annotations=[ dict(
                    text="Python code: <a href='https://plot.ly/ipython-notebooks/network-graphs/'> https://plot.ly/ipython-notebooks/network-graphs/</a>",
                    showarrow=False,
                    xref="paper", yref="paper",
                    x=0.005, y=-0.002 ) ],
                xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
                yaxis=dict(showgrid=False, zeroline=False, showticklabels=False))
                )
fig.show()

This creates an interactive chart like:

enter image description here

Source: Network graphs

like image 41
Ali Cirik Avatar answered Oct 17 '22 04:10

Ali Cirik