【3】数据分析-4-2-由煮面条到图论到图论--igraph

igrpah

一、安装

py2安装

pip install python-igraph

二、教学示例

2.1 Igraph网络图创建与优化

以中国古代战国七雄为对象(点),不同国家若地理位置相邻,则认为存在地理联系(边),以此为基准,试着创建一个简单的抽象网络图(这里只探究两两相邻关系,不保证地理方位的正确性):

# 创建一个空对象
g = igraph.Graph()
# 添加网络中的点
vertex = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
g.add_vertices(vertex)
# 添加网络中的边
edges = [('a', 'c'), ('a', 'e'), ('a', 'b'), ('b', 'd'), ('b', 'g'), ('c', 'e'),
         ('d', 'f'), ('d', 'g'), ('e', 'f'), ('e', 'g'), ('f', 'g')]
g.add_edges(edges)
# -----------------------其它信息-----------------------------
# 国家名称
g.vs['label'] = ['齐', '楚', '燕', '韩', '赵', '魏', '秦']
# 国家大致相对面积(为方便显示没有采用真实面积)
g.vs['aera'] = [50, 100, 70, 40, 60, 40, 80]
# 统计日期
g['Date'] = '公元前279年'
# -----------------------简单作图-----------------------------
# 选择图的布局方式
layout = g.layout('kk')
# 用Igraph内置函数绘图
igraph.plot(g, layout)

战国势力地理关系图1.0

这张图不是很美观,那么如何去优化和完善它呢?比如说点的颜色、大小,边的长短、粗细等。可以通过调节igraph.plot()中的参数达到我们想要的效果:

# -----------------------设置参数-----------------------------
# 参数集合。visual_style是一个参数字典,可以动态添加想要个性化设定的参数
visual_style = {}
# 根据相对面积,设置点的大小
visual_style["vertex_size"] = g.vs['aera']
# 根据国家实力,设置点的颜色
visual_style["vertex_color"] = [color_map[power] for power in g.vs["power"]]
# 边的粗细(这里随机生成)
visual_style['edge_width'] = [2 + 5*np.random.rand() for i in np.arange(11)]
# 图尺寸
visual_style["bbox"] = (600, 480)
# 边缘距离
visual_style["margin"] = 50
# 布局方式
visual_style["layout"] = layout
# -----------------------画图-----------------------------
igraph.plot(g, **visual_style)

PS:看懂以上代码,就了解了Igraph中基本的网络图的构建方法,以及可视化展示的常用参数设置方法。接下来,重点讲讲如何从这些网络图中找出一些有价值的信息。

2.2 网络图信息价值挖掘

那么,基于这张图,有哪些有价值的信息可以挖掘呢?

1.节点的度(degree)。

概念:某节点的度,只与该节点有直接联系的其它点的个数。在有向图中,度还可分为出度(out-defree)和进度(in-degree),它一定程度上表现了某节点周围的密集程度。 在本例中,度的实际含义是一个国家的邻国数量。

# 点的度
numbers = g.degree()
# 不同国家邻国数量
neighbors = dict(zip(g.vs['label'], numbers))
print(neighbors)

输出结果:

{‘魏’: 3, ‘燕’: 2, ‘赵’: 4, ‘楚’: 3, ‘齐’: 3, ‘韩’: 3, ‘秦’: 4}

2.中介中心性(betweenness)。

概念:一个结点担任其它两个结点之间最短路路劲的桥梁的次数,次数越多,该数越大。 在本例中指的是国家起到的枢纽作用大小。

# 计算中介中心性
betweenness = g.betweenness()
# 保留一位小数
betweenness = [round(i, 1) for i in betweenness]
# 与国家名对应
country_betweenness = dict(zip(g.vs['label'], betweenness))
print('不同国家的中介中心性(枢纽作用):\n', country_betweenness)

输出结果:

不同国家的中介中心性(枢纽作用):
 {'魏': 0.8, '燕': 0.0, '赵': 4.2, '楚': 1.8, '齐': 1.8, '韩': 0.5, '秦': 1.8}

3.另一个非常常见的操作是找两个点之间的最短路径。

概念:任选两个节点,连通这两个节点的最少边数,定义为这两个节点的最短路径长度。 当然,这里所指的最短路径,只用来表示不同国家间的抽象地理关系,和实际中的最短路径含义并不一致。

#计算魏国和齐国的最短路径(如有多条路径,只取其中之一)

path = g.get_shortest_paths('c', 'd')[0]
seq = g.vs.select(path)

print('燕韩之间的最短路径: ', seq['label'])

输出文字结果:

燕韩之间的最短路径:  ['燕', '齐', '楚', '韩']

将最短路径用红色标记:

# --------------------------路径-----------------------------
path = seq['name']
# ---------------------给边设定颜色---------------------------
# 默认为黑色
edge_color = dict(zip(edges, ['black']*11))
# 最短路径里的边映射为红色。映射时需要考虑元组中对象顺序,这里按字母从小到大排序
for i in np.arange(np.size(path)-1):
    if path[i] < path[i+1]:
        edge_color[(path[i], path[i + 1])] = 'red'
    else:
        edge_color[(path[i + 1], path[i])] = 'red'
visual_style['edge_color'] = [edge_color[edge] for edge in edges]
# ------------------------画图------------------------------
m = igraph.plot(g, **visual_style)

四、我的案例

4.1

代码示例

import igraph
import pandas as pd
from igraph import *
import matplotlib.pyplot as plt
import numpy as np

print(igraph.__version__)
g = Graph()

input_fp = 'summary/cell_info_merge_sort.xlsx'
df = pd.read_excel(input_fp)

df['gemline'] = df['HC_blastp_hit'].str.split('_',expand=True)[0] + '-' + df['LC_blastp_hit'].str.split('_',expand=True)[0]

df = df[df['choosed']==1]

df['cdr_num_normal'] = df['cdr_num'].apply(np.log)

max_cdr = df['cdr_num_normal'].max()

colors = plt.get_cmap('OrRd')

pair_edge = []
vertex = []
verter_hash = {}
germline_hit = {}
for index_1,row_1 in df.iterrows():
    germline = row_1['gemline']
    barcode = row_1['barcode']
    umis_min = row_1['umis_min']
    cdrs = row_1['cdr_num_normal']
    
    pair_edge.append((row_1['barcode'],row_1['gemline']))
    distance = 200 - row_1['HC_blastp_identity_1(%)'] -row_1['LC_blastp_identity_1(%)']
    vertex.append(germline)    
    vertex.append(barcode)
    
    if germline not in verter_hash:
        verter_hash[germline] = {}
    if barcode not in verter_hash:
        verter_hash[barcode] ={}
    verter_hash[germline]['size'] =1
    verter_hash[germline]['edge_arrow_size'] = distance
    verter_hash[germline]['vertex_label'] = ''
    verter_hash[germline]['vertex_label_size'] = 1
    
    color  = colors(0.1)
    verter_hash[germline]['vertex_color'] = color
    
    
    verter_hash[barcode]['size'] = umis_min *20
    verter_hash[barcode]['edge_arrow_size'] = distance
    
    verter_hash[barcode]['vertex_label'] = ''
    verter_hash[barcode]['vertex_label_size'] = 1 
    color  = colors(np.true_divide(cdrs,max_cdr))
    verter_hash[barcode]['vertex_color'] = color
    
    
    if germline not in germline_hit:
        germline_hit[germline] = 0
    germline_hit[germline] +=1

#label    
germline_hit_sorted = sorted(germline_hit.items(),key=lambda vv:vv[1],reverse=True )[:5]
# germline_hit_sorted = ['test1','test2','test3']

for one_key in    germline_hit_sorted:
    verter_hash[one_key[0]]['vertex_label'] = one_key[0]
    verter_hash[one_key[0]]['vertex_label_size'] = 10
    
vertex = list(set(vertex))

# vertex = [0,1,2]
g.add_vertices(vertex)
g.add_edges(pair_edge)


layout = g.layout_fruchterman_reingold()   # 这个版布局是为了所有的cluster的线条不交叉

visual_style = {}
visual_style["layout"] = layout

visual_style["vertex_size"] = [verter_hash[ii]['size'] for ii in vertex]
visual_style["edge_arrow_size"] = [verter_hash[ii]['edge_arrow_size'] for ii in vertex]

visual_style["vertex_label"]  = [verter_hash[ii]['vertex_label'] for ii in vertex]
visual_style["vertex_color"]  = [verter_hash[ii]['vertex_color'] for ii in vertex]

visual_style["vertex_label_size"]  = [verter_hash[ii]['vertex_label_size'] for ii in vertex]



plot(g, **visual_style)

参考资料

药企,独角兽,苏州。团队长期招人,感兴趣的都可以发邮件聊聊:tiehan@sina.cn
个人公众号,比较懒,很少更新,可以在上面提问题,如果回复不及时,可发邮件给我: tiehan@sina.cn