「Python数据分析」 社会网络:无向有权图生成带权重的边列表(遇到浅拷贝的坑)

赋值、浅拷贝、深拷贝方法集合在文末。

输出结果

前两列为构成边的节点,第三列为该边的权重。
边

遇到的问题及解决方法

先贴一开始犯错的代码。
这个函数实现的功能是输入一个由边组成的二维列表,输出所有不重复的边以及权重(重复次数)。用于后续的社会网络分析与指标计算,因为是合作网络,所以不存在方向关系。

生成边列表的方法:生成边列表edges_list

def get_weighted_links(edges_list):
    ''' edges_list:是包含边的列表,[[节点1,节点2],...],边可以重复,重复次数记为权重
        返回值:带权重的边列表,[[节点1,节点2,weight],...]
    '''
	weighted_links = [] # 作为返回值
	exist_links = [] # 记录当前已经出现过的边
    for link in edges_list:
        if link not in exist_links:
        	exist_links.append(link)
            link.append(1) # 新边weight置1
            weighted_links.append(link)            
        else:
            # exsit_links和weighted_links里的列表顺序是一样的
            index = exist_links.index(link) 
            # 已存在的边,weight + 1
            weighted_links[index][2] = weighted_links[index][2] + 1 
    return weighted_links

调用这个函数,把输出存入csv之后,发现没有像预期的重复的边被合并并记录为权重,而是所有的边原封不动的输出了而且权重全部是1。

这就意味着函数执行过程中没有进入过else,也就是说所有新输入的边都不存在在exist_links中。这很奇怪,于是我输出了一下exist_links的前面几项。

print(exist_links[0:5])

结果发现exist_links里的link竟然也被append加了权重?

[['ANDREW-SMITH R', 'KATER P', 1], ['ANIMAS CORP.', 'LOCKHEED MARTIN CORP.', 1], ['ANIMAS CORP.', 'APG KK', 1], ['ANIMAS CORP.', 'ASEPSIS INC.', 1], ['ANIMAS CORP.', 'AQUA CLEAR INDUSTRIES LLC', 1]]

我又在调用结束时输出了一下原始输入links,也被改了。

[['1 A LIFESAFER INC', 'MONITECH INC', 1], ['3D HYRLIFTAR AB', 'BEIJING ZXWORLD CORP.', 1], ['3M CO', 'HONDA MOTOR CO. LTD.(HONDA GIKEN KOGYO KK)', 1], ['7188501 CANADA INC', 'HYDROGENICS CORP.', 1], ['A. RAYMOND GROUP', 'BOVILLE DANIEL', 1]]

我意识到问题可能出在对link的append操作上。即上面代码中的这一部分:

# 相同函数部分省略
			exist_links.append(link)
            link.append(1)
            weighted_links.append(link) 
# 相同函数部分省略

事实上,无论是原始links存储的,还是给exist_links或是csv_links传入的,都是每个link首地址的指针,因此,不管是什么操作顺序,一旦link被改变,这些列表中的值都会一起改变。

这都是列表浅拷贝的锅。
列表操作中,同样是浅拷贝的其他操作见文末。这里言归正传。

因此我又重新生成了一个新列表,进行了赋值。这次应该不会改变原列表了吧?

# 相同函数部分省略
			exist_links.append(link)
			_link = link
            _link.append(1)
            weighted_links.append(_link) 
# 相同函数部分省略

结果发现,不对,还是改变了。(感觉事情突然严重了)。难道赋值操作存的也是指针?

不,赋值操作甚至谈不上拷贝,就是直接引用。此时两个列表是等价的,修改其中任何一个都会影响另一个。

因此在我的函数里,如果一定想要用这种操作顺序,代码就要使用列表的深拷贝。所谓深拷贝就是会重新开辟一块存储空间给新变量,这个时候两个列表就互不影响。深拷贝使用copy模块中的deepcopy()方法实现。

from copy import deepcopy
# 相同函数部分省略
        	exist_links.append(link)
            # 不能直接link.append(1),exsit_link里的link也会变
            _link = deepcopy(link)  # 深拷贝
            _link.append(1)  
            weighted_links.append(_link)
# 相同函数部分省略       

这样修改之后,就可以实现函数的预期功能了。

还有另一种不使用深拷贝的改法。给exist_links存的link切片,links改变无所谓。

# 相同函数部分省略
        	link.append(1)
            weighted_links.append(link)
            exist_links.append(link[:2]) # 只取link前两项
# 相同函数部分省略       

完整能正常实现功能的代码如下。最好还是用deepcopy,不然会把输入的内容给改了,比较危险。

def get_weighted_links(edges_list):
    ''' edges_list:是包含边列表的矩阵,[[节点1,节点2],...],边可以重复,重复次数记为权重
        返回值:带权重的边列表,[[节点1,节点2,weight],...]
    '''
    weighted_links = []
    exist_links = []
    for link in edges_list:
        if link not in exist_links:
            exist_links.append(link)
            _link = deepcopy(link)  # 深拷贝
            _link.append(1)  
            weighted_links.append(_link)
        else:
            index = exist_links.index(link) 
            weighted_links[index][2] = weighted_links[index][2] + 1 
    return weighted_links

赋值

赋值是非拷贝方法。赋值只是对另一个列表的引用,两个列表等价。

list_test = [1,2,3,4,5,6]

l1 = list_test     

Python中不明确区分赋值和引用,一般对静态变量的传递为赋值(字符串,整数,元组),对动态变量的传递为引用(列表,字典)。

浅拷贝操作集合。

浅拷贝是拷贝一层,深层次的对象级别的就拷贝引用,因此当原对象发生变化的时候,拷贝对象也跟着变化;深拷贝是拷贝多层,每一级别的数据都会拷贝出来,更改原对象,拷贝对象不会发生变化。
简单来说:浅拷贝相关的两个列表相互影响,对其中一个修改,另一个同时被修改。

import copy

# 1.切片
l2 = list_test[1:3]
# 2.list()方法
l3 = list(list_test)
# 3.循环调用赋值
l4 = [i for i in [i for i in L0]]
# 4. extend方法
l5 = []
l5.extend(list_test)
# 5.append方法
l6 = l4.append(list_test)
# 6.copy模块中的copy()方法
l7 = copy(list_test)

深拷贝

给变量开辟新存储空间,两个列表互不影响。

from copy import deepcopy
l8 = deepcopy(list_test)     
  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值