Python 图算法系列3-矩阵计算图指标

0 说明

使用矩阵方法计算图指标。
FWCN(Frequency-Weighted Common Neighbor):
A和B之间的距离由他们的共同邻居决定。

数据:facebook在Kaggle上的一些公开数据

1 处理

确保具有如下两个包
pip3 install python-louvain 【社区发现】
pip3 install networkx 【网络计算】

读入数据

import networkx as nx 
import numpy as np 
import pandas as pd 

with open('./facebook-combined.txt', 'r') as f:
    data = f.readlines()
data[:3]

In [3]: data[:3]                                                                
Out[3]: ['0 1\n', '0 2\n', '0 3\n']
# 前三行, 节点0,1 节点0,2, 节点0,3 三条边

In [2]: len(data)                                                               
Out[2]: 88234

# 作为图导入
fb = nx.read_edgelist('./facebook-combined.txt',
                      create_using=nx.Graph(), nodetype=int)
# 图的基本信息
print(nx.info(fb))
In [7]: print(nx.info(fb))                                                      
Name: 
Type: Graph
Number of nodes: 4039
Number of edges: 88234
Average degree:  43.6910


# ***** 图的展示 *****
# 参考:https://www.kaggle.com/pierrek20/social-network-visualization-and-analysis
nodes = fb.nodes()
degree = fb.degree()
colors = [degree[n] for n in nodes]

pos = nx.kamada_kawai_layout(fb)
cmap = plt.cm.viridis_r
cmap = plt.cm.Greys


fig = plt.figure(figsize=(15, 9), dpi=100)

nx.draw(fb, pos, alpha=0.8, nodelist=nodes, node_color='w', node_size=10,
        with_labels=False, font_size=6, width=0.2, cmap=cmap, edge_color='yellow')
fig.set_facecolor('#0B243B')

plt.legend()
plt.show()

在这里插入图片描述

2 图的分割

图很容易就会变的非常大,而许多图的算法时间复杂度是比较高的,有些点并不需要加入甲酸。因此要先进行子图分割,然后再计算。

图的分割可以分为硬分割和软分割两种。硬分割是指通过图中显示的边连接将图划分成子图(求连通子图),软分割是指将连通图中的一些边拆掉,形成一批新的子图(社区发现)。

In [12]: # 图的连通性  
    ...: def get_conngraph(C): 
    ...:     res = [x for x in sorted(nx.connected_components(C), key=len, rever
    ...: se=True)] 
    ...:     return res 
    ...: cong = get_conngraph(fb) 
    ...:                                                                        

In [13]: len(cong)                                                              
Out[13]: 1

可见,这批数据是一个连通图。因此尝试进行社区分割。模块度(Modularity)用来衡量一个社区的划分是不是相对比较好的结果。一个相对好的结果在社区内部的节点相似度较高,而在社区外部节点的相似度较低。

关于Networkx和Community包的用法可以参考这篇文章

# 图的社区分割
import community
partition = community.best_partition(fb)
node_cluster = pd.Series(partition)
mod = community.modularity(partition,fb)
print('Modularity is ' ,mod)
'''
In[18]: node_cluster.value_counts()
Out[18]:
7     548
4     535
1     457
2     446
3     442
0     350
5     323
9     237
12    226
11    206
6     117
8      73
10     60
13     19
dtype: int64
'''
In [19]: mod = community.modularity(partition,fb) 
    ...: print('Modularity is ' ,mod)                                           
Modularity is  0.834789224221449

可以看到连通图已经被分成了14个社区(Cluster), 需要注意的是:

  1. 社区不一定是连通图
  2. 有时候社区不可分(依据模块度判定)
# 选取一个社区,查看其连通性
c4 = fb.subgraph(set(node_cluster[node_cluster==4].index))
c4g = get_conngraph(c4)

# 这个社区是连通的
In [26]: len(c4g)                                                               
Out[26]: 1

# 获取(极大)连通子图
c4g0 = c4.subgraph(c4g[0])
#查看图信息
print(nx.info(c4g0))

In [31]: # 获取(极大)连通子图 
    ...: c4g0 = c4.subgraph(c4g[0]) 
    ...: #查看图信息 
    ...: print(nx.info(c4g0))                                                   
Name: 
Type: Graph
Number of nodes: 535
Number of edges: 8691
Average degree:  32.4897

经过分割处理后,现在我们可以在一张较小的图上进行计算。将上面的画图函数封装一下:

def draw_a_graph(g, filename,path='./'):
    nodes = g.nodes()
    degree = g.degree()
    colors = [degree[n] for n in nodes]

    pos = nx.kamada_kawai_layout(g)
    cmap = plt.cm.viridis_r
    cmap = plt.cm.Greys


    fig = plt.figure(figsize=(15, 9), dpi=100)

    nx.draw(getattr, pos, alpha=0.8, nodelist=nodes, node_color='w', node_size=10,
            with_labels=False, font_size=6, width=0.2, cmap=cmap, edge_color='yellow')
    fig.set_facecolor('#0B243B')
    plt.savefig(path + filename)
    print('File Saved ', path+filename)
    return True

看一下这个社区:
在这里插入图片描述

3 FWCN计算

点和点之间的距离怎么度量?一种度量方式是通过节点对(Node Pair)的共同邻居来判断。公式如下:

FWCN:
在这里插入图片描述

# 随便选取一个度不太多的点 -->2678
'''
In [6]: degreeg                                                                 
Out[6]: DegreeView({2661: 99, 2662: 37, 2665: 50, 2667: 20, 
2668: 32, 2671: 6, 2672: 37, 2674: 70, 2675: 38, 2677: 14, 
2678: 9, 2682: 16, 2684: 28, 2685: 12, 2686: 24, 2687: 21, 
2688: 11, 2690: 30, 2691: 3, 2692: 5, 2693: 18, 2696: 19, 2697
'''
g1_nodes = nx.neighbors(g, 2678)
g1_nodes = set(g1_nodes) |{2678}
g1 = g.subgraph(g1_nodes)
print('Graph Info', nx.info(g1))
draw_a_graph(g1, 'tem.png', node_size=2000, width=1, with_labels=True,font_size=10)


Graph Info Name: 
Type: Graph
Number of nodes: 10
Number of edges: 38 (9~ 81)
Average degree:   7.6000

可以得到这样一张比较小的图:
在这里插入图片描述
现在我们关注2678-2760这两个点的距离(FWCN)。我们先把这张图中的边打印出来,这意味着我们总有办法把2678和2760的边找出来:
在这里插入图片描述
或者直接使用函数寻找这两个点的邻居:

# 2678的邻居
n2678 = list(nx.neighbors(g1, 2678))
print(n2678)

# 2760的邻居
n2760 = list(nx.neighbors(g1, 2760))
print(n2760)

In [44]: # 2678的邻居 
    ...: n2678 = list(nx.neighbors(g1, 2678)) 
    ...: print(n2678) 
    ...:  
    ...: # 2760的邻居 
    ...: n2760 = list(nx.neighbors(g1, 2760)) 
    ...: print(n2760)                                                           
[990, 1505, 1684, 1726, 2760, 3057, 3164, 3222, 3263]
[990, 1505, 1684, 2678, 3057, 3222, 3263]


In [45]: # 2678和2760的共同邻居 CN of Two Nodes 
    ...: cn2678_2760 = list( set(n2678) & set(n2760)) 
    ...: print(cn2678_2760)                                                     
[1505, 3057, 1684, 3222, 990, 3263]

在这里插入图片描述
可以看到,2678和2760共有6个共同邻居(Common Neighbors)。下面可以根据公式,计算2678和2760的FWCN。

# 计算如下
# 计算FWCN
w = 0 
cn_g = g1
for i,cn in enumerate(cn2678_2760):
    print('%i Common Node :%s' % (i,cn ))
    tem_de = cn_g.degree(cn)
    tem_w = np.log(tem_de)**-1
    print('Degree :', tem_de)
    print('Weight Added :', tem_w)
    w+=tem_w
    print('Accumulated Weight', w)

# ---
0 Common Node :1505
Degree : 5
Weight Added : 0.6213349345596119
Accumulated Weight 0.6213349345596119
1 Common Node :3057
Degree : 8
Weight Added : 0.48089834696298783
Accumulated Weight 1.1022332815225997
2 Common Node :1684
Degree : 9
Weight Added : 0.45511961331341866
Accumulated Weight 1.5573528948360185
3 Common Node :3222
Degree : 7
Weight Added : 0.5138983423697507
Accumulated Weight 2.071251237205769
4 Common Node :990
Degree : 9
Weight Added : 0.45511961331341866
Accumulated Weight 2.5263708505191875
5 Common Node :3263
Degree : 8
Weight Added : 0.48089834696298783
Accumulated Weight 3.0072691974821755

最终获得了2678和2760在子图g1下的FWCN。接下来,我们计算在社区4的子图(c4g0)下计算所有点之间的FWCN。

4 计算子图所有节点对的FWCN (For循环)

封装FWCN的计算函数如下:

def cal_FWCN(g, A, B):
    # A的邻居
    An = list(nx.neighbors(g, A))
    # B的邻居
    Bn = list(nx.neighbors(g, B))
    # A和B的共同邻居 CN of Two Nodes
    ABcn = list(set(An) & set(Bn))
    # 计算FWCN
    w = 0
    if len(ABcn):
        for cn in ABcn:
            tem_de = g.degree(cn)
            tem_w = np.log(tem_de)**-1
            w += tem_w
    return w
# 测试
In [63]: cal_FWCN(g1, 2678,2760)                                                
Out[63]: 3.0072691974821755

开始载入数据并进行计算:

import networkx as nx
import numpy as np
import pandas as pd
from fb_func1 import draw_a_graph
from fb1 import from_pickle, to_pickle
import matplotlib.pyplot as plt
from fb_func2 import cal_FWCN
from itertools import combinations
# 1 载入图
g = from_pickle('temg')
# 2 计算节点对
nodes = set(g.nodes)
node_pairs = list(combinations(nodes,2))

# 一共有14万对组合(但是不是所有组合都有边)
In [61]: len(node_pairs)                                                        
Out[61]: 142845


# 3 开始计算
import time 
start = time.time()
res_list = []
for p in node_pairs:
    fwcn = cal_FWCN(g, p[0],p[1])
    if fwcn >0:
        tem_list = [p[0],p[1],fwcn]
        res_list.append(tem_list)
print('For Iteration Takes %.3f' %(time.time() - start))

For Iteration Takes 111.431 Seconds

# 由于网络比较紧密,大部分的节点间都有FWCN距离
In [65]: len(res_list)                                                          
Out[65]: 142360

可以看得出来,针对一个仅有500个节点的网络就需要计算那么长的时间。期间也就增加了几百兆的内存开销。从空间换时间的角度上,我们看看使用矩阵计算可以带来怎样的结果。

Before:
在这里插入图片描述

After:
在这里插入图片描述

5 计算子图所有节点对的FWCN (矩阵计算)

如果有使用For循环进行SumProduct的都可以使用矩阵实现。FWCN很明显是计算了节点的CN(Common Neighbors)之和,然后在外层也有一个For循环进行所有节点的一个便利。这两个操作都可以使用矩阵来替代。

FB这个例子的网相对稠密,而有些例子的网不那么稠密,这就意味着并不是所有节点对的FWCN都值得计算。
所以使用矩阵替代的化有三个点:

  1. 确认哪些点对(Node Pairs)需要计算
  2. 如何使用向量相乘来控制点对的计算(多个点对的操作可以堆叠在一个矩阵里)
  3. 如何使用点乘来一次性计算某个节点对的FWCN
import networkx as nx
import numpy as np
import pandas as pd
from fb_func1 import draw_a_graph
from fb1 import from_pickle, to_pickle
import matplotlib.pyplot as plt

g = from_pickle('temg')
g1_nodes = nx.neighbors(g, 2678)
g1_nodes = set(g1_nodes) | {2678}
g1 = g.subgraph(g1_nodes)
print('Graph Info', nx.info(g1))

# 2678的邻居
n2678 = list(nx.neighbors(g1, 2678))

# 2760的邻居
n2760 = list(nx.neighbors(g1, 2760))

# 2678和2760的共同邻居 CN of Two Nodes
cn2678_2760 = list(set(n2678) & set(n2760))
# CN Vector 
cn_v1 = np.array([g1.degree(x) for x in cn2678_2760])
cn_v2 = np.log(cn_v1) ** -1
print(cn_v2.sum())


In [74]: print(cn_v2.sum())                                                     
3.0072691974821755

# 将图转为邻接矩阵,向量的顺序图节点度的顺序
In [89]: print(pd.Series(g1.degree)) 
    ...: g1m = np.array(nx.adjacency_matrix(g1).todense()) 
    ...: print(g1m) 
    ...: print(g1m.sum(axis=0))                                                 
0    (1505, 5)
1    (2760, 7)
2    (3057, 8)
3    (1684, 9)
4     (990, 9)
5    (3222, 7)
6    (2678, 9)
7    (3164, 7)
8    (1726, 7)
9    (3263, 8)
dtype: object
[[0 1 0 1 1 0 1 1 0 0]
 [1 0 1 1 1 1 1 0 0 1]
 [0 1 0 1 1 1 1 1 1 1]
 [1 1 1 0 1 1 1 1 1 1]
 [1 1 1 1 0 1 1 1 1 1]
 [0 1 1 1 1 0 1 0 1 1]
 [1 1 1 1 1 1 0 1 1 1]
 [1 0 1 1 1 0 1 0 1 1]
 [0 0 1 1 1 1 1 1 0 1]
 [0 1 1 1 1 1 1 1 1 0]]
[5 7 8 9 9 7 9 7 7 8]


# 根据度,计算g1节点的度向量
node_list = []
degree_list = []
for x in g1.degree:
    node_list.append(x[0])
    degree_list.append(x[1])

# 节点向量
node_vec = np.log(np.array(degree_list))**-1

In [103]: node_vec                                                              
Out[103]: 
array([0.62133493, 0.51389834, 0.48089835, 0.45511961, 0.45511961,
       0.51389834, 0.45511961, 0.51389834, 0.51389834, 0.48089835])

# 2760,2678节点的选择向量
sel2760_2678 = np.array([0, 1, 0, 0, 0, 0, 1, 0, 0, 0])

# 选择2760和2678的共同向量(可以按行相乘,起到类似mask的作用)
sel2760_2678_mat = g1m * sel2760_2678


In [100]: sel2760_2678_mat                                                      
Out[100]: 
array([[0, 1, 0, 0, 0, 0, 1, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 1, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 1, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 1, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 1, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 1, 0, 0, 0]], dtype=int64)

# 将每行的向量与选择向量比较
sel2760_2678_mat1 = sel2760_2678_mat == sel2760_2678

# 比较的中间结果
In [106]: sel2760_2678_mat1                                                     
Out[106]: 
array([[ True,  True,  True,  True,  True,  True,  True,  True,  True,
         True],
       [ True, False,  True,  True,  True,  True,  True,  True,  True,
         True],
       [ True,  True,  True,  True,  True,  True,  True,  True,  True,
         True],
       [ True,  True,  True,  True,  True,  True,  True,  True,  True,
         True],
       [ True,  True,  True,  True,  True,  True,  True,  True,  True,
         True],
       [ True,  True,  True,  True,  True,  True,  True,  True,  True,
         True],
       [ True,  True,  True,  True,  True,  True, False,  True,  True,
         True],
       [ True, False,  True,  True,  True,  True,  True,  True,  True,
         True],
       [ True, False,  True,  True,  True,  True,  True,  True,  True,
         True],
       [ True,  True,  True,  True,  True,  True,  True,  True,  True,
         True]])

# 如果每一行的每个元素都和选择向量对应,那么按横向相加的值等于选则向量长度
sel2760_2678_mat2 = sel2760_2678_mat1.sum(axis=1) == len(sel2760_2678)

# 可以看看选择的变量
np.array(node_list)[sel2760_2678_mat2]
# 和for循环的版本比较是相同
In [114]: np.array(node_list)[sel2760_2678_mat2]                                
Out[114]: array([1505, 3057, 1684,  990, 3222, 3263])
# 使用点乘来计算该节点对的FWCN
res = np.dot(sel2760_2678_mat2, node_vec)

In [115]: res                                                                   
Out[115]: 3.0072691974821755


# 同时计算多个向量
In [116]: node_list                                                             
Out[116]: [1505, 2760, 3057, 1684, 990, 3222, 2678, 3164, 1726, 3263]

sel2760_2678 = [0, 1, 0, 0, 0, 0, 1, 0, 0, 0]
sel1505_3057 = [1, 0, 1, 0, 0, 0, 0, 0, 0, 0]

pair_list = []
pair_list.append(sel2760_2678)
pair_list.append(sel1505_3057)

pair_mat = np.array(pair_list)

In [126]: pair_mat                                                              
Out[126]: 
array([[0, 1, 0, 0, 0, 0, 1, 0, 0, 0],
       [1, 0, 1, 0, 0, 0, 0, 0, 0, 0]])
# g1m : 10*10 , pair_mat :2*10

g2m = np.dot(g1m, pair_mat.T)

In [127]: g2m                                                                   
Out[127]: 
array([[2, 0],
       [1, 2],
       [2, 0],
       [2, 2],
       [2, 2],
       [2, 1],
       [1, 2],
       [1, 2],
       [1, 1],
       [2, 1]], dtype=int64)
# 因为是Node Pair,所以mask留下为2的是匹配的
g2m1 = g2m ==2

# g2m1:10*2
print(g2m1.shape)

In [128]: g2m1                                                                  
Out[128]: 
array([[ True, False],
       [False,  True],
       [ True, False],
       [ True,  True],
       [ True,  True],
       [ True, False],
       [False,  True],
       [False,  True],
       [False, False],
       [ True, False]])
# 点乘,批量计算Node Pair的结果
np.dot(g2m1.T,node_vec)

In [129]: np.dot(g2m1.T,node_vec)                                               
Out[129]: array([3.0072692 , 2.39315552])
# 使用之前的函数计算,验证
In [130]: cal_FWCN(g1, 1505, 3057)                                              
Out[130]: 2.3931555246797576

# BTW, 如果要从选择向量中获取实际选中的变量
np.array(node_list)[np.argwhere(np.array(sel2760_2678) ==1)].ravel()

In [148]: np.array(node_list)[np.argwhere(np.array(sel2760_2678) ==1)].ravel()  
Out[148]: array([2760, 2678])

以上基本上确定了计算方法,下面完善几个细节:

  1. 函数1:给到节点列表,给出选择矩阵
  2. 函数2:给到选择矩阵和邻接矩阵,计算FWCN
  3. 函数3:结合选择矩阵和FWCN给出节点对(Node Pair)的FWCN
函数1from itertools import combinations
def create_sel_mat(vec):
    pos = list(range(len(vec)))
    combs = combinations(pos, 2)
    matlist = []
    for x in combs:
        temp = np.zeros(len(vec))
        temp[x[0]] = 1
        temp[x[1]] = 1
        matlist.append(temp)
    return np.array(matlist), combs

tem1,tem2 = create_sel_mat(node_list)

tem1 = 选择矩阵,作为函数2的入参
tem2 = 节点对,作为函数3的入参

In [163]: tem1                                                                  
Out[163]: 
array([[1., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
       [1., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
       [1., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
       [1., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
       [1., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [1., 0., 0., 0., 0., 0., 1., 0., 0., 0.],
       [1., 0., 0., 0., 0., 0., 0., 1., 0., 0.],


In [168]: tem2                                                                  
Out[168]: 
[(0, 1),
 (0, 2),
 (0, 3),
 (0, 4),
 (0, 5),
 (0, 6),
 (0, 7),
 (0, 8),

# 函数2
# 根据度,计算g1节点的度向量
node_list = []
degree_list = []
for x in g1.degree:
    node_list.append(x[0])
    degree_list.append(x[1])

# 节点向量
node_vec = np.log(np.array(degree_list))**-1
# 邻接矩阵
adj_mat = np.array(nx.adjacency_matrix(g1).todense())
# 1选择矩阵
# 2节点的度向量
def mat_cal_FWCN(sel_mat, adj_mat,node_vec):
    tem_mat = np.dot(adj_mat, sel_mat.T)
    tem_mat1 = tem_mat == 2
    return np.dot(tem_mat1.T, node_vec)
     
tem3 = mat_cal_FWCN(tem1,adj_mat,node_vec)

In [170]: tem3                                                                  
Out[170]: 
array([1.36535884, 2.39315552, 1.93803591, 1.93803591, 1.87925718,
       1.93803591, 1.36535884, 1.87925718, 2.39315552, 2.36015553,
       3.0072692 , 3.0072692 , 2.32715553, 3.0072692 , 2.94849047,
       2.84105388, 2.36015553, 3.44673094, 3.44673094, 2.87405387,

# 组装结果
def form_mat_res_df(combs, FWCN,node_list):
    node_dict = dict(zip(range(len(node_list)),node_list ))
    res_df = pd.DataFrame()
    res_df['combs'] = combs
    res_df['FWCN'] = FWCN
    res_df['NodeA'] = res_df['combs'].apply(lambda x: x[0]).map(node_dict)
    res_df['NodeB'] = res_df['combs'].apply(lambda x: x[1]).map(node_dict)
    return res_df

tem4 = form_mat_res_df(tem2, tem3,node_list)

In [185]: tem4                                                                  
Out[185]: 
     combs      FWCN  NodeA  NodeB
0   (0, 1)  1.365359   1505   2760
1   (0, 2)  2.393156   1505   3057
2   (0, 3)  1.938036   1505   1684
3   (0, 4)  1.938036   1505    990
4   (0, 5)  1.879257   1505   3222
5   (0, 6)  1.938036   1505   2678
6   (0, 7)  1.365359   1505   3164
7   (0, 8)  1.879257   1505   1726
8   (0, 9)  2.393156   1505   3263
9   (1, 2)  2.360156   2760   3057
10  (1, 3)  3.007269   2760   1684
11  (1, 4)  3.007269   2760    990

结果比较

下面对For循环和矩阵计算并比较结果。

# 1 载入图
g = from_pickle('temg')
# 2 计算节点对
nodes = set(g.nodes)
node_pairs = list(combinations(nodes,2))

# 3 开始计算 - For
import time 
start = time.time()
res_list = []
for p in node_pairs:
    fwcn = cal_FWCN(g, p[0],p[1])
    # if fwcn >0:
    tem_list = [p[0],p[1],fwcn]
    res_list.append(tem_list)
res_df_by_for = pd.DataFrame(res_list)
res_df_by_for.columns = ['NodeA','NodeB','FWCN']
print('For Iteration Takes %.3f Seconds' % (time.time() - start))
# to_pickle(res_df_by_for, 'res_df_by_for')

# 4 矩阵方式计算
start = time.time()
res_df_by_mat = MatFWCN(g)
print('Mat  Takes %.3f Seconds' % (time.time() - start))

In [214]: run fb3.py                                                            
For Iteration Takes 123.003 Seconds
Mat  Takes 5.236 Seconds

In [216]:  res_df_by_for.head()                                                 
Out[216]: 
   NodeA  NodeB      FWCN
0   2661   2662  2.482988
1   2661   2665  3.191722
2   2661   2667  0.636166
3   2661   2668  1.618131
4   2661   2671  0.159273

In [217]: res_df_by_mat.head()                                                  
Out[217]: 
    combs      FWCN  NodeA  NodeB
0  (0, 1)  2.482988   2661   2662
1  (0, 2)  3.191722   2661   2665
2  (0, 3)  0.636166   2661   2667
3  (0, 4)  1.618131   2661   2668
4  (0, 5)  0.159273   2661   2671

# 精度上有微小差异,忽略不计
In [218]: print('Difference of Two', 
     ...:       (res_df_by_mat['FWCN'].values - res_df_by_for['FWCN'].values).su
     ...: m())                                                                  
Difference of Two -2.1685986340003183e-12

时间上快了至少20倍?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值